Multi-view edit III
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob61180930f9d5c6842d15acad44d3ac5db29c2cc4
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_bReadonlyIsChangable(false)
86 , m_bTarget(false)
87 , m_nCaretGoalPos(0)
88 , m_nSelViewBlockStart(-1)
89 , m_nSelViewBlockEnd(-1)
90 , m_bFocused(FALSE)
91 , m_bShowSelection(true)
92 , texttype(CFileTextLines::AUTOTYPE)
93 , m_bModified(FALSE)
94 , m_bOtherDiffChecked(false)
95 , m_bInlineWordDiff(true)
96 , m_bWhitespaceInlineDiffs(false)
97 , m_pState(NULL)
98 , m_pFindDialog(NULL)
99 , m_nStatusBarID(0)
100 , m_bMatchCase(false)
101 , m_bLimitToDiff(true)
102 , m_bWholeWord(false)
103 , m_pDC(NULL)
104 , m_pWorkingFile(NULL)
106 m_ptCaretViewPos.x = 0;
107 m_ptCaretViewPos.y = 0;
108 m_ptSelectionViewPosStart = m_ptCaretViewPos;
109 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
110 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
111 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
112 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
113 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
114 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
115 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
116 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
117 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
118 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
119 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\""));
120 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
121 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
122 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
123 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
124 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
125 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
126 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
127 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
128 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
129 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
130 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
131 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
132 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
133 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
134 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
136 for (int i=0; i<1024; ++i)
137 m_sConflictedText += _T("??");
138 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
140 m_szTip[0] = 0;
141 m_wszTip[0] = 0;
142 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
143 EnableToolTips();
146 CBaseView::~CBaseView()
148 ReleaseBitmap();
149 DeleteFonts();
150 DestroyIcon(m_hAddedIcon);
151 DestroyIcon(m_hRemovedIcon);
152 DestroyIcon(m_hConflictedIcon);
153 DestroyIcon(m_hConflictedIgnoredIcon);
154 DestroyIcon(m_hWhitespaceBlockIcon);
155 DestroyIcon(m_hEqualIcon);
156 DestroyIcon(m_hLineEndingCR);
157 DestroyIcon(m_hLineEndingCRLF);
158 DestroyIcon(m_hLineEndingLF);
159 DestroyIcon(m_hEditedIcon);
160 DestroyIcon(m_hMovedIcon);
161 DestroyCursor(m_margincursor);
164 BEGIN_MESSAGE_MAP(CBaseView, CView)
165 ON_WM_VSCROLL()
166 ON_WM_HSCROLL()
167 ON_WM_ERASEBKGND()
168 ON_WM_CREATE()
169 ON_WM_DESTROY()
170 ON_WM_SIZE()
171 ON_WM_MOUSEWHEEL()
172 ON_WM_MOUSEHWHEEL()
173 ON_WM_SETCURSOR()
174 ON_WM_KILLFOCUS()
175 ON_WM_SETFOCUS()
176 ON_WM_CONTEXTMENU()
177 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
178 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
179 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
180 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
181 ON_WM_KEYDOWN()
182 ON_WM_LBUTTONDOWN()
183 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
184 ON_WM_MOUSEMOVE()
185 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
186 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
187 ON_WM_CHAR()
188 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
189 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
190 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
191 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
192 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
193 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
194 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
195 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
196 ON_WM_TIMER()
197 ON_WM_LBUTTONDBLCLK()
198 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
199 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
200 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
201 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
202 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
203 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
204 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
205 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
206 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
207 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
208 ON_WM_LBUTTONUP()
209 END_MESSAGE_MAP()
212 void CBaseView::DocumentUpdated()
214 ReleaseBitmap();
215 m_nLineHeight = -1;
216 m_nCharWidth = -1;
217 m_nScreenChars = -1;
218 m_nLastScreenChars = -1;
219 m_nMaxLineLength = -1;
220 m_nScreenLines = -1;
221 m_nTopLine = 0;
222 m_bModified = FALSE;
223 m_bOtherDiffChecked = false;
224 m_nDigits = 0;
225 m_nMouseLine = -1;
226 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
227 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
228 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
229 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
230 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
231 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
232 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
233 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
234 DeleteFonts();
235 ClearCurrentSelection();
236 UpdateStatusBar();
237 Invalidate();
240 void CBaseView::UpdateStatusBar()
242 int nRemovedLines = 0;
243 int nAddedLines = 0;
244 int nConflictedLines = 0;
246 if (m_pViewData)
248 for (int i=0; i<m_pViewData->GetCount(); i++)
250 DiffStates state = m_pViewData->GetState(i);
251 switch (state)
253 case DIFFSTATE_ADDED:
254 case DIFFSTATE_IDENTICALADDED:
255 case DIFFSTATE_MOVED_TO:
256 case DIFFSTATE_THEIRSADDED:
257 case DIFFSTATE_YOURSADDED:
258 case DIFFSTATE_CONFLICTADDED:
259 nAddedLines++;
260 break;
261 case DIFFSTATE_IDENTICALREMOVED:
262 case DIFFSTATE_REMOVED:
263 case DIFFSTATE_MOVED_FROM:
264 case DIFFSTATE_THEIRSREMOVED:
265 case DIFFSTATE_YOURSREMOVED:
266 nRemovedLines++;
267 break;
268 case DIFFSTATE_CONFLICTED:
269 case DIFFSTATE_CONFLICTED_IGNORED:
270 nConflictedLines++;
271 break;
276 CString sBarText;
277 CString sTemp;
279 switch (texttype)
281 case CFileTextLines::ASCII:
282 sBarText = _T("ASCII ");
283 break;
284 case CFileTextLines::BINARY:
285 sBarText = _T("BINARY ");
286 break;
287 case CFileTextLines::UTF16_LE:
288 sBarText = _T("UTF-16LE ");
289 break;
290 case CFileTextLines::UTF16_BE:
291 sBarText = _T("UTF-16BE ");
292 break;
293 case CFileTextLines::UTF32_LE:
294 sBarText = _T("UTF-32LE ");
295 break;
296 case CFileTextLines::UTF32_BE:
297 sBarText = _T("UTF-32BE ");
298 break;
299 case CFileTextLines::UTF8:
300 sBarText = _T("UTF8 ");
301 break;
302 case CFileTextLines::UTF8BOM:
303 sBarText = _T("UTF8 BOM ");
304 break;
307 switch(lineendings)
309 case EOL_LF:
310 sBarText += _T("LF ");
311 break;
312 case EOL_CRLF:
313 sBarText += _T("CRLF ");
314 break;
315 case EOL_LFCR:
316 sBarText += _T("LFCR ");
317 break;
318 case EOL_CR:
319 sBarText += _T("CR ");
320 break;
321 case EOL_VT:
322 sBarText += _T("VT ");
323 break;
324 case EOL_FF:
325 sBarText += _T("FF ");
326 break;
327 case EOL_NEL:
328 sBarText += _T("NEL ");
329 break;
330 case EOL_LS:
331 sBarText += _T("LS ");
332 break;
333 case EOL_PS:
334 sBarText += _T("PS ");
335 break;
336 #ifdef _DEBUG
337 case EOL_AUTOLINE:
338 sBarText += _T("AEOL ");
339 break;
340 #endif
343 if (sBarText.IsEmpty())
344 sBarText += _T(" / ");
346 if (nRemovedLines)
348 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
349 if (!sBarText.IsEmpty())
350 sBarText += _T(" / ");
351 sBarText += sTemp;
353 if (nAddedLines)
355 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
356 if (!sBarText.IsEmpty())
357 sBarText += _T(" / ");
358 sBarText += sTemp;
360 if (nConflictedLines)
362 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
363 if (!sBarText.IsEmpty())
364 sBarText += _T(" / ");
365 sBarText += sTemp;
367 if (m_pwndStatusBar)
369 UINT nID;
370 UINT nStyle;
371 int cxWidth;
372 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
373 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
375 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
377 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
379 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
380 sBarText = sTemp+sBarText;
382 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
384 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
385 sBarText = sTemp+sBarText;
387 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
388 //calculate the width of the text
389 CDC * pDC = m_pwndStatusBar->GetDC();
390 if (pDC)
392 CSize size = pDC->GetTextExtent(sBarText);
393 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
394 ReleaseDC(pDC);
396 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
400 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
402 if (!CView::PreCreateWindow(cs))
403 return FALSE;
405 cs.dwExStyle |= WS_EX_CLIENTEDGE;
406 cs.style &= ~WS_BORDER;
407 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
408 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
410 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
411 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
413 // View must always create its own scrollbars,
414 // if only it's not used within splitter
415 cs.style |= (WS_HSCROLL | WS_VSCROLL);
417 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
418 return TRUE;
421 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
423 int nIndex = 0;
424 if (bBold)
425 nIndex |= 1;
426 if (bItalic)
427 nIndex |= 2;
428 if (m_apFonts[nIndex] == NULL)
430 m_apFonts[nIndex] = new CFont;
431 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
432 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
433 m_lfBaseFont.lfItalic = (BYTE) bItalic;
434 CDC * pDC = GetDC();
435 if (pDC)
437 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
438 ReleaseDC(pDC);
440 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
441 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
443 delete m_apFonts[nIndex];
444 m_apFonts[nIndex] = NULL;
445 return CView::GetFont();
448 return m_apFonts[nIndex];
451 void CBaseView::CalcLineCharDim()
453 CDC *pDC = GetDC();
454 if (pDC == nullptr)
455 return;
456 CFont *pOldFont = pDC->SelectObject(GetFont());
457 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
458 pDC->SelectObject(pOldFont);
459 ReleaseDC(pDC);
461 m_nLineHeight = szCharExt.cy;
462 if (m_nLineHeight <= 0)
463 m_nLineHeight = -1;
464 m_nCharWidth = szCharExt.cx;
465 if (m_nCharWidth <= 0)
466 m_nCharWidth = -1;
469 int CBaseView::GetScreenChars()
471 if (m_nScreenChars == -1)
473 CRect rect;
474 GetClientRect(&rect);
475 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
476 if (m_nScreenChars < 0)
477 m_nScreenChars = 0;
479 return m_nScreenChars;
482 int CBaseView::GetAllMinScreenChars() const
484 int nChars = INT_MAX;
485 if (IsLeftViewGood())
486 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
487 if (IsRightViewGood())
488 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
489 if (IsBottomViewGood())
490 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
491 return (nChars==INT_MAX) ? 0 : nChars;
494 int CBaseView::GetAllMaxLineLength() const
496 int nLength = 0;
497 if (IsLeftViewGood())
498 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
499 if (IsRightViewGood())
500 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
501 if (IsBottomViewGood())
502 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
503 return nLength;
506 int CBaseView::GetLineHeight()
508 if (m_nLineHeight == -1)
509 CalcLineCharDim();
510 if (m_nLineHeight <= 0)
511 return 1;
512 return m_nLineHeight;
515 int CBaseView::GetCharWidth()
517 if (m_nCharWidth == -1)
518 CalcLineCharDim();
519 if (m_nCharWidth <= 0)
520 return 1;
521 return m_nCharWidth;
524 int CBaseView::GetMaxLineLength()
526 if (m_nMaxLineLength == -1)
528 m_nMaxLineLength = 0;
529 int nLineCount = GetLineCount();
530 for (int i=0; i<nLineCount; i++)
532 int nActualLength = GetLineLength(i);
533 if (m_nMaxLineLength < nActualLength)
534 m_nMaxLineLength = nActualLength;
537 return m_nMaxLineLength;
540 int CBaseView::GetLineLength(int index)
542 if (m_pViewData == NULL)
543 return 0;
544 if (m_pViewData->GetCount() == 0)
545 return 0;
546 if ((int)m_Screen2View.size() <= index)
547 return 0;
548 int viewLine = GetViewLineForScreen(index);
549 if (m_pMainFrame->m_bWrapLines)
551 int nLineLength = GetLineChars(index).GetLength();
552 ASSERT(nLineLength >= 0);
553 return nLineLength;
555 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
556 ASSERT(nLineLength >= 0);
557 return nLineLength;
560 int CBaseView::GetViewLineLength(int nViewLine) const
562 if (m_pViewData == NULL)
563 return 0;
564 if (m_pViewData->GetCount() <= nViewLine)
565 return 0;
566 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
567 ASSERT(nLineLength >= 0);
568 return nLineLength;
571 int CBaseView::GetLineCount() const
573 if (m_pViewData == NULL)
574 return 1;
575 int nLineCount = (int)m_Screen2View.size();
576 ASSERT(nLineCount >= 0);
577 return nLineCount;
580 int CBaseView::GetSubLineOffset(int index)
582 return m_Screen2View.GetSubLineOffset(index);
585 CString CBaseView::GetViewLineChars(int nViewLine) const
587 if (m_pViewData == NULL)
588 return 0;
589 if (m_pViewData->GetCount() <= nViewLine)
590 return 0;
591 return m_pViewData->GetLine(nViewLine);
594 CString CBaseView::GetLineChars(int index)
596 if (m_pViewData == NULL)
597 return 0;
598 if (m_pViewData->GetCount() == 0)
599 return 0;
600 if ((int)m_Screen2View.size() <= index)
601 return 0;
602 int viewLine = GetViewLineForScreen(index);
603 if (m_pMainFrame->m_bWrapLines)
605 int subLine = GetSubLineOffset(index);
606 if (subLine >= 0)
608 if (subLine < CountMultiLines(viewLine))
610 return m_ScreenedViewLine[viewLine].SubLines[subLine];
612 return L"";
615 return m_pViewData->GetLine(viewLine);
618 void CBaseView::CheckOtherView()
620 if (m_bOtherDiffChecked)
621 return;
622 // find out what the 'other' file is
623 m_pOtherViewData = NULL;
624 m_pOtherView = NULL;
625 if (this == m_pwndLeft && IsRightViewGood())
627 m_pOtherViewData = m_pwndRight->m_pViewData;
628 m_pOtherView = m_pwndRight;
631 if (this == m_pwndRight && IsLeftViewGood())
633 m_pOtherViewData = m_pwndLeft->m_pViewData;
634 m_pOtherView = m_pwndLeft;
637 m_bOtherDiffChecked = true;
641 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
643 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
644 ASSERT(viewData);
646 DiffStates origstate = viewData->GetState(nLineIndex);
648 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
649 nStartBlock = nLineIndex;
650 nEndBlock = nLineIndex;
651 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
653 DiffStates state = viewData->GetState(nStartBlock - 1);
654 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
655 origstate = state;
656 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
657 nStartBlock--;
658 else
659 break;
661 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
663 DiffStates state = viewData->GetState(nEndBlock + 1);
664 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
665 origstate = state;
666 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
667 nEndBlock++;
668 else
669 break;
673 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
675 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
676 int len = 0;
677 for (int i = nStartBlock; i <= nEndBlock; ++i)
678 len += viewData->GetLine(i).GetLength();
680 CString block;
681 // do not check for whitespace blocks if the line is too long, because
682 // reserving a lot of memory here takes too much time (performance hog)
683 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
684 return block;
685 block.Preallocate(len+1);
686 for (int i = nStartBlock; i <= nEndBlock; ++i)
687 block += viewData->GetLine(i);
688 return block;
691 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
693 if (m_pViewData == NULL)
694 return false;
695 bIdentical = false;
696 CheckOtherView();
697 if (!m_pOtherViewData)
698 return false;
699 int viewLine = GetViewLineForScreen(nLineIndex);
700 if (
701 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
702 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
705 bIdentical = true;
706 return false;
708 // first check whether the line itself only has whitespace changes
709 CString mine = m_pViewData->GetLine(viewLine);
710 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
711 if (mine.IsEmpty() && other.IsEmpty())
713 bIdentical = true;
714 return false;
717 if (mine == other)
719 bIdentical = true;
720 return true;
722 FilterWhitespaces(mine, other);
723 if (mine == other)
724 return true;
726 int nStartBlock1, nEndBlock1;
727 int nStartBlock2, nEndBlock2;
728 GetWhitespaceBlock(m_pViewData, viewLine, nStartBlock1, nEndBlock1);
729 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
730 mine = GetWhitespaceString(m_pViewData, nStartBlock1, nEndBlock1);
731 if (mine.IsEmpty())
732 bIdentical = false;
733 else
735 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
736 bIdentical = mine == other;
737 FilterWhitespaces(mine, other);
740 return (!mine.IsEmpty()) && (mine == other);
743 bool CBaseView::IsViewLineHidden(int nViewLine)
745 return IsViewLineHidden(m_pViewData, nViewLine);
748 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
750 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
753 int CBaseView::GetLineNumber(int index) const
755 if (m_pViewData == NULL)
756 return -1;
757 int viewLine = GetViewLineForScreen(index);
758 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
759 return -1;
760 return m_pViewData->GetLineNumber(viewLine);
763 int CBaseView::GetScreenLines()
765 if (m_nScreenLines == -1)
767 SCROLLBARINFO sbi;
768 sbi.cbSize = sizeof(sbi);
769 int scrollBarHeight = 0;
770 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
771 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
772 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
773 scrollBarHeight = 0;
774 CRect rect;
775 GetClientRect(&rect);
776 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
777 if (m_nScreenLines < 0)
778 m_nScreenLines = 0;
780 return m_nScreenLines;
783 int CBaseView::GetAllMinScreenLines() const
785 int nLines = INT_MAX;
786 if (IsLeftViewGood())
787 nLines = m_pwndLeft->GetScreenLines();
788 if (IsRightViewGood())
789 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
790 if (IsBottomViewGood())
791 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
792 return (nLines==INT_MAX) ? 0 : nLines;
795 int CBaseView::GetAllLineCount() const
797 int nLines = 0;
798 if (IsLeftViewGood())
799 nLines = m_pwndLeft->GetLineCount();
800 if (IsRightViewGood())
801 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
802 if (IsBottomViewGood())
803 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
804 return nLines;
807 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
809 if (IsLeftViewGood())
810 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
811 if (IsRightViewGood())
812 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
813 if (IsBottomViewGood())
814 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
817 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
819 SCROLLINFO si;
820 si.cbSize = sizeof(si);
821 if (bPositionOnly)
823 si.fMask = SIF_POS;
824 si.nPos = m_nTopLine;
826 else
828 EnableScrollBarCtrl(SB_VERT, TRUE);
829 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
831 m_nTopLine = 0;
832 Invalidate();
834 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
835 si.nMin = 0;
836 si.nMax = GetAllLineCount();
837 si.nPage = GetAllMinScreenLines();
838 si.nPos = m_nTopLine;
840 VERIFY(SetScrollInfo(SB_VERT, &si));
843 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
845 CView::OnVScroll(nSBCode, nPos, pScrollBar);
846 if (m_pwndLeft)
847 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
848 if (m_pwndRight)
849 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
850 if (m_pwndBottom)
851 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
852 if (m_pwndLocator)
853 m_pwndLocator->Invalidate();
856 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
858 // Note we cannot use nPos because of its 16-bit nature
859 SCROLLINFO si;
860 si.cbSize = sizeof(si);
861 si.fMask = SIF_ALL;
862 VERIFY(master->GetScrollInfo(SB_VERT, &si));
864 int nPageLines = GetScreenLines();
865 int nLineCount = GetLineCount();
867 int nNewTopLine;
869 static LONG textwidth = 0;
870 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
871 switch (nSBCode)
873 case SB_TOP:
874 nNewTopLine = 0;
875 break;
876 case SB_BOTTOM:
877 nNewTopLine = nLineCount - nPageLines + 1;
878 break;
879 case SB_LINEUP:
880 nNewTopLine = m_nTopLine - 1;
881 break;
882 case SB_LINEDOWN:
883 nNewTopLine = m_nTopLine + 1;
884 break;
885 case SB_PAGEUP:
886 nNewTopLine = m_nTopLine - si.nPage + 1;
887 break;
888 case SB_PAGEDOWN:
889 nNewTopLine = m_nTopLine + si.nPage - 1;
890 break;
891 case SB_THUMBPOSITION:
892 m_ScrollTool.Clear();
893 nNewTopLine = si.nTrackPos;
894 textwidth = 0;
895 break;
896 case SB_THUMBTRACK:
897 nNewTopLine = si.nTrackPos;
898 if (GetFocus() == this)
900 RECT thumbrect;
901 GetClientRect(&thumbrect);
902 ClientToScreen(&thumbrect);
904 POINT thumbpoint;
905 thumbpoint.x = thumbrect.right;
906 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
907 m_ScrollTool.Init(&thumbpoint);
908 if (textwidth == 0)
910 CString sTemp = sFormat;
911 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
912 textwidth = m_ScrollTool.GetTextWidth(sTemp);
914 thumbpoint.x -= textwidth;
915 int line = GetLineNumber(nNewTopLine);
916 if (line >= 0)
917 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
918 else
919 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
921 break;
922 default:
923 return;
926 if (nNewTopLine < 0)
927 nNewTopLine = 0;
928 if (nNewTopLine >= nLineCount)
929 nNewTopLine = nLineCount - 1;
930 ScrollToLine(nNewTopLine);
933 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
935 if (IsLeftViewGood())
936 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
937 if (IsRightViewGood())
938 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
939 if (IsBottomViewGood())
940 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
943 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
945 SCROLLINFO si;
946 si.cbSize = sizeof(si);
947 if (bPositionOnly)
949 si.fMask = SIF_POS;
950 si.nPos = m_nOffsetChar;
952 else
954 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
955 if (!m_pMainFrame->m_bWrapLines)
957 int minScreenChars = GetAllMinScreenChars();
958 int maxLineLength = GetAllMaxLineLength();
959 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
961 m_nOffsetChar = 0;
962 Invalidate();
964 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
965 si.nMin = 0;
966 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
967 si.nMax += GetMarginWidth()/GetCharWidth();
968 si.nPage = minScreenChars;
969 si.nPos = m_nOffsetChar;
972 VERIFY(SetScrollInfo(SB_HORZ, &si));
975 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
977 CView::OnHScroll(nSBCode, nPos, pScrollBar);
978 if (m_pwndLeft)
979 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
980 if (m_pwndRight)
981 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
982 if (m_pwndBottom)
983 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
984 if (m_pwndLocator)
985 m_pwndLocator->Invalidate();
988 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
990 SCROLLINFO si;
991 si.cbSize = sizeof(si);
992 si.fMask = SIF_ALL;
993 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
995 int nPageChars = GetScreenChars();
996 int nMaxLineLength = GetMaxLineLength();
998 int nNewOffset;
999 switch (nSBCode)
1001 case SB_LEFT:
1002 nNewOffset = 0;
1003 break;
1004 case SB_BOTTOM:
1005 nNewOffset = nMaxLineLength - nPageChars + 1;
1006 break;
1007 case SB_LINEUP:
1008 nNewOffset = m_nOffsetChar - 1;
1009 break;
1010 case SB_LINEDOWN:
1011 nNewOffset = m_nOffsetChar + 1;
1012 break;
1013 case SB_PAGEUP:
1014 nNewOffset = m_nOffsetChar - si.nPage + 1;
1015 break;
1016 case SB_PAGEDOWN:
1017 nNewOffset = m_nOffsetChar + si.nPage - 1;
1018 break;
1019 case SB_THUMBPOSITION:
1020 case SB_THUMBTRACK:
1021 nNewOffset = si.nTrackPos;
1022 break;
1023 default:
1024 return;
1027 if (nNewOffset >= nMaxLineLength)
1028 nNewOffset = nMaxLineLength - 1;
1029 if (nNewOffset < 0)
1030 nNewOffset = 0;
1031 ScrollToChar(nNewOffset, TRUE);
1034 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1036 if (m_nOffsetChar != nNewOffsetChar)
1038 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1039 m_nOffsetChar = nNewOffsetChar;
1040 CRect rcScroll;
1041 GetClientRect(&rcScroll);
1042 rcScroll.left += GetMarginWidth();
1043 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1044 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1045 // update the view header
1046 rcScroll.left = 0;
1047 rcScroll.top = 0;
1048 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1049 InvalidateRect(&rcScroll, FALSE);
1050 UpdateWindow();
1051 if (bTrackScrollBar)
1052 RecalcHorzScrollBar(TRUE);
1053 UpdateCaret();
1054 if (m_pwndLineDiffBar)
1055 m_pwndLineDiffBar->Invalidate();
1059 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1061 if (m_pwndLeft)
1062 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1063 if (m_pwndRight)
1064 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1065 if (m_pwndBottom)
1066 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1069 void CBaseView::ScrollAllSide(int delta)
1071 int nNewOffset = m_nOffsetChar;
1072 nNewOffset += delta;
1073 int nMaxLineLength = GetMaxLineLength();
1074 if (nNewOffset >= nMaxLineLength)
1075 nNewOffset = nMaxLineLength - 1;
1076 if (nNewOffset < 0)
1077 nNewOffset = 0;
1078 ScrollAllToChar(nNewOffset, TRUE);
1079 if (m_pwndLineDiffBar)
1080 m_pwndLineDiffBar->Invalidate();
1081 UpdateCaret();
1084 void CBaseView::ScrollSide(int delta)
1086 int nNewOffset = m_nOffsetChar;
1087 nNewOffset += delta;
1088 int nMaxLineLength = GetMaxLineLength();
1089 if (nNewOffset >= nMaxLineLength)
1090 nNewOffset = nMaxLineLength - 1;
1091 if (nNewOffset < 0)
1092 nNewOffset = 0;
1093 ScrollToChar(nNewOffset, TRUE);
1094 if (m_pwndLineDiffBar)
1095 m_pwndLineDiffBar->Invalidate();
1096 UpdateCaret();
1099 void CBaseView::ScrollVertical(short zDelta)
1101 const int nLineCount = GetLineCount();
1102 int nTopLine = m_nTopLine;
1103 nTopLine -= (zDelta/30);
1104 if (nTopLine < 0)
1105 nTopLine = 0;
1106 if (nTopLine >= nLineCount)
1107 nTopLine = nLineCount - 1;
1108 ScrollToLine(nTopLine, TRUE);
1111 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1113 if (m_nTopLine != nNewTopLine)
1115 if (nNewTopLine < 0)
1116 nNewTopLine = 0;
1118 int nScrollLines = m_nTopLine - nNewTopLine;
1120 m_nTopLine = nNewTopLine;
1121 CRect rcScroll;
1122 GetClientRect(&rcScroll);
1123 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1124 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1125 UpdateWindow();
1126 if (bTrackScrollBar)
1127 RecalcVertScrollBar(TRUE);
1128 UpdateCaret();
1133 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1135 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1137 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1139 int nViewLine = GetViewLineForScreen(nLineIndex);
1140 HICON icon = NULL;
1141 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1142 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1143 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1145 DiffStates state = m_pViewData->GetState(nViewLine);
1146 switch (state)
1148 case DIFFSTATE_ADDED:
1149 case DIFFSTATE_THEIRSADDED:
1150 case DIFFSTATE_YOURSADDED:
1151 case DIFFSTATE_IDENTICALADDED:
1152 case DIFFSTATE_CONFLICTADDED:
1153 eIcon = TScreenedViewLine::ICN_ADD;
1154 break;
1155 case DIFFSTATE_REMOVED:
1156 case DIFFSTATE_THEIRSREMOVED:
1157 case DIFFSTATE_YOURSREMOVED:
1158 case DIFFSTATE_IDENTICALREMOVED:
1159 eIcon = TScreenedViewLine::ICN_REMOVED;
1160 break;
1161 case DIFFSTATE_CONFLICTED:
1162 eIcon = TScreenedViewLine::ICN_CONFLICT;
1163 break;
1164 case DIFFSTATE_CONFLICTED_IGNORED:
1165 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1166 break;
1167 case DIFFSTATE_EDITED:
1168 eIcon = TScreenedViewLine::ICN_EDIT;
1169 break;
1170 case DIFFSTATE_MOVED_TO:
1171 case DIFFSTATE_MOVED_FROM:
1172 eIcon = TScreenedViewLine::ICN_MOVED;
1173 break;
1174 default:
1175 break;
1177 bool bIdentical = false;
1178 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
1180 if (bIdentical)
1181 eIcon = TScreenedViewLine::ICN_SAME;
1182 else
1183 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1185 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1187 switch (eIcon)
1189 case TScreenedViewLine::ICN_UNKNOWN:
1190 case TScreenedViewLine::ICN_NONE:
1191 break;
1192 case TScreenedViewLine::ICN_SAME:
1193 icon = m_hEqualIcon;
1194 break;
1195 case TScreenedViewLine::ICN_EDIT:
1196 icon = m_hEditedIcon;
1197 break;
1198 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1199 icon = m_hWhitespaceBlockIcon;
1200 break;
1201 case TScreenedViewLine::ICN_ADD:
1202 icon = m_hAddedIcon;
1203 break;
1204 case TScreenedViewLine::ICN_CONFLICT:
1205 icon = m_hConflictedIcon;
1206 break;
1207 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1208 icon = m_hConflictedIgnoredIcon;
1209 break;
1210 case TScreenedViewLine::ICN_REMOVED:
1211 icon = m_hRemovedIcon;
1212 break;
1213 case TScreenedViewLine::ICN_MOVED:
1214 icon = m_hMovedIcon;
1215 break;
1219 if (icon)
1221 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1223 if ((m_bViewLinenumbers)&&(m_nDigits))
1225 int nSubLine = GetSubLineOffset(nLineIndex);
1226 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1227 CString sLinenumber;
1228 if (bIsFirstSubline)
1230 CString sLinenumberFormat;
1231 int nLineNumber = GetLineNumber(nLineIndex);
1232 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1234 // TODO: do not show if there is no number hidden
1235 // TODO: show number if there is only one
1236 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1237 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1239 else if (nLineNumber >= 0)
1241 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1242 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1244 else if (m_pMainFrame->m_bWrapLines)
1246 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1247 sLinenumber.Format(sLinenumberFormat, _T("·"));
1249 if (!sLinenumber.IsEmpty())
1251 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1252 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1254 pdc->SelectObject(GetFont());
1255 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1262 int CBaseView::GetMarginWidth()
1264 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1266 if (m_nDigits <= 0)
1268 int nLength = (int)m_pViewData->GetCount();
1269 // find out how many digits are needed to show the highest line number
1270 CString sMax;
1271 sMax.Format(_T("%d"), nLength);
1272 m_nDigits = sMax.GetLength();
1274 int nWidth = GetCharWidth();
1275 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1277 return MARGINWIDTH;
1280 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1282 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1283 COLORREF crBk, crFg;
1284 if (IsBottomViewGood())
1286 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1287 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1289 else
1291 DiffStates state = DIFFSTATE_REMOVED;
1292 if (this == m_pwndRight)
1294 state = DIFFSTATE_ADDED;
1296 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1298 pdc->SetBkColor(crBk);
1299 pdc->FillSolidRect(textrect, crBk);
1301 pdc->SetTextColor(crFg);
1303 pdc->SelectObject(GetFont(FALSE, TRUE));
1305 CString sViewTitle;
1306 if (IsModified())
1308 sViewTitle = _T("* ") + m_sWindowName;
1310 else
1312 sViewTitle = m_sWindowName;
1314 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1315 if (nStringLength > rect.Width())
1317 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1318 sViewTitle = m_sWindowName.Mid(offset);
1320 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1321 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1322 if (this->GetFocus() == this)
1323 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1324 else
1325 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1328 void CBaseView::OnDraw(CDC * pDC)
1330 CRect rcClient;
1331 GetClientRect(rcClient);
1333 int nLineCount = GetLineCount();
1334 int nLineHeight = GetLineHeight();
1336 CDC cacheDC;
1337 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1338 if (m_pCacheBitmap == NULL)
1340 m_pCacheBitmap = new CBitmap;
1341 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1343 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1345 DrawHeader(pDC, rcClient);
1347 CRect rcLine;
1348 rcLine = rcClient;
1349 rcLine.top += nLineHeight+HEADERHEIGHT;
1350 rcLine.bottom = rcLine.top + nLineHeight;
1351 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1352 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1354 int nCurrentLine = m_nTopLine;
1355 bool bBeyondFileLineCached = false;
1356 while (rcLine.top < rcClient.bottom)
1358 if (nCurrentLine < nLineCount)
1360 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1361 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1362 bBeyondFileLineCached = false;
1364 else if (!bBeyondFileLineCached)
1366 DrawMargin(&cacheDC, rcCacheMargin, -1);
1367 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1368 bBeyondFileLineCached = true;
1371 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1373 nCurrentLine ++;
1374 rcLine.OffsetRect(0, nLineHeight);
1377 cacheDC.SelectObject(pOldBitmap);
1378 cacheDC.DeleteDC();
1381 bool CBaseView::IsStateConflicted(DiffStates state)
1383 switch (state)
1385 case DIFFSTATE_CONFLICTED:
1386 case DIFFSTATE_CONFLICTED_IGNORED:
1387 case DIFFSTATE_CONFLICTEMPTY:
1388 case DIFFSTATE_CONFLICTADDED:
1389 return true;
1391 return false;
1394 bool CBaseView::IsStateEmpty(DiffStates state)
1396 switch (state)
1398 case DIFFSTATE_CONFLICTEMPTY:
1399 case DIFFSTATE_UNKNOWN:
1400 case DIFFSTATE_EMPTY:
1401 return true;
1403 return false;
1406 bool CBaseView::IsStateRemoved(DiffStates state)
1408 switch (state)
1410 case DIFFSTATE_REMOVED:
1411 case DIFFSTATE_MOVED_FROM:
1412 case DIFFSTATE_THEIRSREMOVED:
1413 case DIFFSTATE_YOURSREMOVED:
1414 case DIFFSTATE_IDENTICALREMOVED:
1415 return true;
1417 return false;
1420 DiffStates CBaseView::ResolveState(DiffStates state)
1422 if (IsStateConflicted(state))
1424 if (state == DIFFSTATE_CONFLICTEMPTY)
1425 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1426 else
1427 return DIFFSTATE_CONFLICTRESOLVED;
1429 return state;
1433 bool CBaseView::IsLineEmpty(int nLineIndex)
1435 if (m_pViewData == 0)
1436 return FALSE;
1437 int nViewLine = GetViewLineForScreen(nLineIndex);
1438 return IsViewLineEmpty(nViewLine);
1441 bool CBaseView::IsViewLineEmpty(int nViewLine)
1443 if (m_pViewData == 0)
1444 return FALSE;
1445 const DiffStates state = m_pViewData->GetState(nViewLine);
1446 return IsStateEmpty(state);
1449 bool CBaseView::IsLineRemoved(int nLineIndex)
1451 if (m_pViewData == 0)
1452 return FALSE;
1453 int nViewLine = GetViewLineForScreen(nLineIndex);
1454 return IsViewLineRemoved(nViewLine);
1457 bool CBaseView::IsViewLineRemoved(int nViewLine)
1459 if (m_pViewData == 0)
1460 return FALSE;
1461 const DiffStates state = m_pViewData->GetState(nViewLine);
1462 return IsStateRemoved(state);
1465 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1467 if (m_pViewData == 0)
1468 return false;
1469 const DiffStates state = m_pViewData->GetState(nLineIndex);
1470 return IsStateConflicted(state);
1473 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1475 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1478 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1480 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1483 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1485 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1486 return;
1487 int viewLine = GetViewLineForScreen(nLineIndex);
1488 EOL ending = m_pViewData->GetLineEnding(viewLine);
1489 if (m_bIconLFs)
1491 HICON hEndingIcon = NULL;
1492 switch (ending)
1494 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1495 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1496 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1497 default: return;
1499 if (origin.x < (rc.left-GetCharWidth()))
1500 return;
1501 // If EOL style has changed, color end-of-line markers as inline differences.
1503 m_bShowInlineDiff && m_pOtherViewData &&
1504 (viewLine < m_pOtherViewData->GetCount()) &&
1505 (ending != EOL_NOENDING) &&
1506 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1507 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1510 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1513 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1515 else
1517 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1518 CPen * oldpen = pDC->SelectObject(&pen);
1519 int yMiddle = origin.y + rc.Height()/2;
1520 int xMiddle = origin.x+GetCharWidth()/2;
1521 bool bMultiline = false;
1522 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1524 if (GetLineLength(nLineIndex+1))
1526 // multiline
1527 bMultiline = true;
1528 pDC->MoveTo(origin.x, yMiddle-2);
1529 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1530 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1531 pDC->LineTo(origin.x, yMiddle+2);
1533 else if (GetLineLength(nLineIndex) == 0)
1534 bMultiline = true;
1536 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1537 bMultiline = true;
1539 if (!bMultiline)
1541 switch (ending)
1543 case EOL_AUTOLINE:
1544 case EOL_CRLF:
1545 // arrow from top to middle+2, then left
1546 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1547 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1548 case EOL_CR:
1549 // arrow from right to left
1550 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1551 pDC->LineTo(origin.x, yMiddle);
1552 pDC->LineTo(origin.x+4, yMiddle+4);
1553 pDC->MoveTo(origin.x, yMiddle);
1554 pDC->LineTo(origin.x+4, yMiddle-4);
1555 break;
1556 case EOL_LFCR:
1557 // from right-upper to left then down
1558 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1559 pDC->LineTo(xMiddle, yMiddle-2);
1560 pDC->LineTo(xMiddle, rc.bottom-1);
1561 pDC->LineTo(xMiddle+4, rc.bottom-5);
1562 pDC->MoveTo(xMiddle, rc.bottom-1);
1563 pDC->LineTo(xMiddle-4, rc.bottom-5);
1564 break;
1565 case EOL_LF:
1566 // arrow from top to bottom
1567 pDC->MoveTo(xMiddle, rc.top);
1568 pDC->LineTo(xMiddle, rc.bottom-1);
1569 pDC->LineTo(xMiddle+4, rc.bottom-5);
1570 pDC->MoveTo(xMiddle, rc.bottom-1);
1571 pDC->LineTo(xMiddle-4, rc.bottom-5);
1572 break;
1573 case EOL_FF: // Form Feed, U+000C
1574 case EOL_NEL: // Next Line, U+0085
1575 case EOL_LS: // Line Separator, U+2028
1576 case EOL_PS: // Paragraph Separator, U+2029
1577 // draw a horizontal line at the bottom of this line
1578 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1579 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1580 pDC->LineTo(origin.x, rc.bottom-2);
1581 pDC->LineTo(origin.x+5, rc.bottom-2);
1582 pDC->MoveTo(origin.x, rc.bottom-2);
1583 pDC->LineTo(origin.x+1, rc.bottom-6);
1584 break;
1585 default: // other EOLs
1586 // arrow from top right to bottom left
1587 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1588 pDC->LineTo(origin.x, rc.bottom-1);
1589 pDC->LineTo(origin.x+5, rc.bottom-2);
1590 pDC->MoveTo(origin.x, rc.bottom-1);
1591 pDC->LineTo(origin.x+1, rc.bottom-6);
1592 break;
1593 case EOL_NOENDING:
1594 break;
1597 pDC->SelectObject(oldpen);
1601 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1603 if (!m_bShowSelection)
1604 return;
1606 int nSelBlockStart;
1607 int nSelBlockEnd;
1608 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1609 return;
1611 const int THICKNESS = 2;
1612 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1614 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1615 int nSubLine = GetSubLineOffset(nLineIndex);
1616 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1617 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1619 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1622 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1623 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1625 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1629 void CBaseView::DrawTextLine(
1630 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1632 ASSERT(nLineIndex < GetLineCount());
1633 int nViewLine = GetViewLineForScreen(nLineIndex);
1634 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1636 LineColors lineCols = GetLineColors(nViewLine);
1638 CString sViewLine = GetViewLineChars(nViewLine);
1639 // mark selection
1640 if (m_bShowSelection && HasTextSelection())
1642 // has this line selection ?
1643 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1645 int nViewLineLength = sViewLine.GetLength();
1647 // first suppose the whole line is selected
1648 int selectedStart = 0;
1649 int selectedEnd = nViewLineLength;
1651 // the view line is partially selected
1652 if (m_ptSelectionViewPosStart.y == nViewLine)
1654 selectedStart = m_ptSelectionViewPosStart.x;
1657 if (m_ptSelectionViewPosEnd.y == nViewLine)
1659 selectedEnd = m_ptSelectionViewPosEnd.x;
1661 // apply selection coloring
1662 // First enforce start and end point
1663 lineCols.SplitBlock(selectedStart);
1664 lineCols.SplitBlock(selectedEnd);
1665 // change color of affected parts
1666 long intenseColorScale = m_bFocused ? 70 : 30;
1667 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1668 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1670 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1671 if (it->second.shot == it->second.background)
1673 it->second.shot = crBk;
1675 it->second.background = crBk;
1676 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1681 // TODO: remove duplicate from selection and mark
1682 if (!m_sMarkedWord.IsEmpty())
1684 int nMarkLength = m_sMarkedWord.GetLength();
1685 //int nViewLineLength = sViewLine.GetLength();
1686 const TCHAR * text = sViewLine;
1687 const TCHAR * findText = text;
1688 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1690 int nMarkStart = static_cast<int>(findText - text);
1691 int nMarkEnd = nMarkStart + nMarkLength;
1692 // First enforce start and end point
1693 lineCols.SplitBlock(nMarkStart);
1694 lineCols.SplitBlock(nMarkEnd);
1695 // change color of affected parts
1696 const long int nIntenseColorScale = 200;
1697 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1698 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1700 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1701 if (it->second.shot == it->second.background)
1703 it->second.shot = crBk;
1705 it->second.background = crBk;
1706 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1708 findText += nMarkLength;
1711 if (!m_sFindText.IsEmpty())
1713 int nMarkStart = 0;
1714 int nMarkEnd = 0;
1715 int nStringPos = nMarkStart;
1716 CString searchLine = sViewLine;
1717 if (!m_bMatchCase)
1718 searchLine.MakeLower();
1719 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1721 // First enforce start and end point
1722 lineCols.SplitBlock(nMarkStart+nStringPos);
1723 lineCols.SplitBlock(nMarkEnd+nStringPos);
1724 // change color of affected parts
1725 const long int nIntenseColorScale = 30;
1726 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1727 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1729 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1730 if (it->second.shot == it->second.background)
1732 it->second.shot = crBk;
1734 it->second.background = crBk;
1735 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1737 searchLine = searchLine.Mid(nMarkEnd);
1738 nStringPos = nMarkEnd;
1742 // @ this point we may cache data for next line which may be same in wrapped mode
1744 int nTextOffset = 0;
1745 int nSubline = GetSubLineOffset(nLineIndex);
1746 for (int n=0; n<nSubline; n++)
1748 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1749 nTextOffset += sLine.GetLength();
1752 CString sLine = GetLineChars(nLineIndex);
1753 int nLineLength = sLine.GetLength();
1754 CString sLineExp = ExpandChars(sLine);
1755 LPCTSTR textExp = sLineExp;
1756 //int nLineLengthExp = sLineExp.GetLength();
1757 int nStartExp = 0;
1758 int nLeft = coords.x;
1759 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1761 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1762 ++itEnd;
1763 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1764 int nEnd = nLineLength;
1765 if (itEnd != lineCols.end())
1767 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1769 int nBlockLength = nEnd - nStart;
1770 if (nBlockLength > 0 && nEnd>=0)
1772 pDC->SetBkColor(itStart->second.background);
1773 pDC->SetTextColor(itStart->second.text);
1774 int nEndExp = CountExpandedChars(sLine, nEnd);
1775 int nTextLength = nEndExp - nStartExp;
1776 LPCTSTR p_zBlockText = textExp + nStartExp;
1777 SIZE Size;
1778 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1779 int nRight = nLeft + Size.cx;
1780 if ((nRight > rc.left) && (nLeft < rc.right))
1782 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1783 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1784 // is 4094 (4095 doesn't work anymore).
1785 // So we limit the length here to that 4094 chars.
1786 // In case we're scrolled to the right, there's no need to draw the string
1787 // from way outside our window, so we also offset the drawing to the start of the window.
1788 // This reduces the string length as well.
1789 int offset = 0;
1790 int leftcoord = nLeft;
1791 if (nLeft < 0)
1793 offset = (-nLeft/GetCharWidth());
1794 nTextLength -= offset;
1795 leftcoord = nLeft % GetCharWidth();
1798 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1799 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1801 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1804 nLeft = nRight;
1805 coords.x = nRight;
1806 nStartExp = nEndExp;
1811 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1813 if (nLineIndex >= GetLineCount())
1814 nLineIndex = -1;
1815 ASSERT(nLineIndex >= -1);
1817 if ((nLineIndex == -1) || !m_pViewData)
1819 // Draw line beyond the text
1820 COLORREF crBkgnd, crText;
1821 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1822 pDC->FillSolidRect(rc, crBkgnd);
1823 return;
1826 int viewLine = GetViewLineForScreen(nLineIndex);
1827 if (m_pMainFrame->m_bCollapsed)
1829 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1831 COLORREF crBkgnd, crText;
1832 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1833 pDC->FillSolidRect(rc, crBkgnd);
1835 const int THICKNESS = 2;
1836 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1837 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1838 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1839 pDC->SetBkColor(crBkgnd);
1840 CRect rect = rc;
1841 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1842 return;
1846 DiffStates diffState = m_pViewData->GetState(viewLine);
1847 COLORREF crBkgnd, crText;
1848 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1850 if (diffState == DIFFSTATE_CONFLICTED)
1852 // conflicted lines are shown without 'text' on them
1853 CRect rect = rc;
1854 pDC->FillSolidRect(rc, crBkgnd);
1855 // now draw some faint text patterns
1856 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1857 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1858 DrawBlockLine(pDC, rc, nLineIndex);
1859 return;
1862 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1863 CString sLine = GetLineChars(nLineIndex);
1864 if (sLine.IsEmpty())
1866 pDC->FillSolidRect(rc, crBkgnd);
1867 DrawBlockLine(pDC, rc, nLineIndex);
1868 DrawLineEnding(pDC, rc, nLineIndex, origin);
1869 return;
1872 CheckOtherView();
1874 // Draw the line
1876 pDC->SelectObject(GetFont(FALSE, FALSE));
1878 DrawTextLine(pDC, rc, nLineIndex, origin);
1880 // draw white space after the end of line
1881 CRect frect = rc;
1882 if (origin.x > frect.left)
1883 frect.left = origin.x;
1884 if (frect.right > frect.left)
1885 pDC->FillSolidRect(frect, crBkgnd);
1887 // draw the whitespace chars
1888 LPCTSTR pszChars = (LPCWSTR)sLine;
1889 if (m_bViewWhitespace)
1891 int xpos = 0;
1892 int y = rc.top + (rc.bottom-rc.top)/2;
1894 int nActualOffset = 0;
1895 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1897 if (*pszChars == _T('\t'))
1898 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1899 else
1900 nActualOffset++;
1901 pszChars++;
1903 if (nActualOffset > m_nOffsetChar)
1904 pszChars--;
1906 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1907 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1908 while (*pszChars)
1910 switch (*pszChars)
1912 case _T('\t'):
1914 // draw an arrow
1915 CPen * oldPen = pDC->SelectObject(&pen);
1916 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1917 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1918 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1919 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1920 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1921 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1922 xpos += nSpaces;
1923 pDC->SelectObject(oldPen);
1925 break;
1926 case _T(' '):
1928 // draw a small dot
1929 CPen * oldPen = pDC->SelectObject(&pen2);
1930 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1931 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1932 xpos++;
1933 pDC->SelectObject(oldPen);
1935 break;
1936 default:
1937 xpos++;
1938 break;
1940 pszChars++;
1943 DrawBlockLine(pDC, rc, nLineIndex);
1944 if (origin.x >= rc.left)
1945 DrawLineEnding(pDC, rc, nLineIndex, origin);
1948 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1950 if (nCount <= 0)
1952 line = _T("");
1953 return;
1956 int nTabSize = GetTabSize();
1958 int nActualOffset = CountExpandedChars(sLine, nOffset);
1960 LPCTSTR pszChars = (LPCWSTR)sLine;
1961 pszChars += nOffset;
1962 int nLength = nCount;
1964 int nTabCount = 0;
1965 for (int i=0; i<nLength; i++)
1967 if (pszChars[i] == _T('\t'))
1968 nTabCount ++;
1971 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1972 int nCurPos = 0;
1973 if (nTabCount > 0 || m_bViewWhitespace)
1975 for (int i=0; i<nLength; i++)
1977 if (pszChars[i] == _T('\t'))
1979 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1980 while (nSpaces > 0)
1982 pszBuf[nCurPos ++] = _T(' ');
1983 nSpaces --;
1986 else
1988 pszBuf[nCurPos] = pszChars[i];
1989 nCurPos ++;
1993 else
1995 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1996 nCurPos = nLength;
1998 pszBuf[nCurPos] = 0;
1999 line.ReleaseBuffer();
2002 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2004 CString sRet;
2005 int nLength = sLine.GetLength();
2006 ExpandChars(sLine, nOffset, nLength, sRet);
2007 return sRet;
2010 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2012 int nTabSize = GetTabSize();
2014 int nActualOffset = 0;
2015 for (int i=0; i<nLength; i++)
2017 if (sLine[i] == _T('\t'))
2018 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2019 else
2020 nActualOffset ++;
2022 return nActualOffset;
2025 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2027 if (m_pwndLeft)
2028 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2029 if (m_pwndRight)
2030 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2031 if (m_pwndBottom)
2032 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2033 if (m_pwndLocator)
2034 m_pwndLocator->Invalidate();
2037 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2039 //almost the same as ScrollAllToLine, but try to put the line in the
2040 //middle of the view, not on top
2041 int nNewTopLine = nNewLine - GetScreenLines()/2;
2042 if (nNewTopLine < 0)
2043 nNewTopLine = 0;
2044 if (nNewTopLine >= (int)m_Screen2View.size())
2045 nNewTopLine = (int)m_Screen2View.size()-1;
2046 if (bAll)
2047 ScrollAllToLine(nNewTopLine);
2048 else
2049 ScrollToLine(nNewTopLine);
2052 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2054 return TRUE;
2057 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2059 if (CView::OnCreate(lpCreateStruct) == -1)
2060 return -1;
2062 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2063 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2064 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2065 m_lfBaseFont.lfHeight = 0;
2066 m_lfBaseFont.lfWeight = FW_NORMAL;
2067 m_lfBaseFont.lfItalic = FALSE;
2068 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2069 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2070 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2071 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2072 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2074 return 0;
2077 void CBaseView::OnDestroy()
2079 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2081 m_pFindDialog->SendMessage(WM_CLOSE);
2082 return;
2084 CView::OnDestroy();
2085 DeleteFonts();
2086 ReleaseBitmap();
2089 void CBaseView::OnSize(UINT nType, int cx, int cy)
2091 CView::OnSize(nType, cx, cy);
2092 ReleaseBitmap();
2094 m_nScreenLines = -1;
2095 m_nScreenChars = -1;
2096 if (m_nLastScreenChars != GetScreenChars())
2098 BuildAllScreen2ViewVector();
2099 m_nLastScreenChars = m_nScreenChars;
2100 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2102 // if we're in wrap mode, the line wrapping most likely changed
2103 // and that means we have to redraw the whole window, not just the
2104 // scrolled part.
2105 Invalidate(FALSE);
2107 else
2109 // make sure the view header is redrawn
2110 CRect rcScroll;
2111 GetClientRect(&rcScroll);
2112 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2113 InvalidateRect(&rcScroll, FALSE);
2116 else
2118 // make sure the view header is redrawn
2119 CRect rcScroll;
2120 GetClientRect(&rcScroll);
2121 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2122 InvalidateRect(&rcScroll, FALSE);
2124 UpdateLocator();
2125 RecalcVertScrollBar();
2126 RecalcHorzScrollBar();
2128 UpdateCaret();
2131 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2133 if (m_pwndLeft)
2134 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2135 if (m_pwndRight)
2136 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2137 if (m_pwndBottom)
2138 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2139 if (m_pwndLocator)
2140 m_pwndLocator->Invalidate();
2141 return CView::OnMouseWheel(nFlags, zDelta, pt);
2144 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2146 if (m_pwndLeft)
2147 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2148 if (m_pwndRight)
2149 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2150 if (m_pwndBottom)
2151 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2152 if (m_pwndLocator)
2153 m_pwndLocator->Invalidate();
2156 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2158 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2159 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2161 if (bControl || bShift)
2163 if (m_pMainFrame->m_bWrapLines)
2164 return;
2165 // Ctrl-Wheel scrolls sideways
2166 ScrollSide(-zDelta/30);
2168 else
2170 ScrollVertical(zDelta);
2174 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2176 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2177 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2179 if (bControl || bShift)
2181 ScrollVertical(zDelta);
2183 else
2185 if (m_pMainFrame->m_bWrapLines)
2186 return;
2187 // Ctrl-Wheel scrolls sideways
2188 ScrollSide(-zDelta/30);
2192 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2194 if (nHitTest == HTCLIENT)
2196 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2198 if (m_nMouseLine < (int)m_Screen2View.size())
2200 if (m_nMouseLine >= 0)
2202 int viewLine = GetViewLineForScreen(m_nMouseLine);
2203 if (viewLine < m_pViewData->GetCount())
2205 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2207 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2208 return TRUE;
2214 if (m_mouseInMargin)
2216 ::SetCursor(m_margincursor);
2217 return TRUE;
2219 if (m_nMouseLine >= 0)
2221 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2222 return TRUE;
2225 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2226 return TRUE;
2228 return CView::OnSetCursor(pWnd, nHitTest, message);
2231 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2233 CView::OnKillFocus(pNewWnd);
2234 m_bFocused = FALSE;
2235 UpdateCaret();
2236 Invalidate();
2239 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2241 CView::OnSetFocus(pOldWnd);
2242 m_bFocused = TRUE;
2243 UpdateCaret();
2244 Invalidate();
2247 int CBaseView::GetLineFromPoint(CPoint point)
2249 ScreenToClient(&point);
2250 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2253 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2255 if (!this->IsWindowVisible())
2256 return;
2258 CIconMenu popup;
2259 if (!popup.CreatePopupMenu())
2260 return;
2262 AddContextItems(popup, state);
2264 CompensateForKeyboard(point);
2266 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2267 ResetUndoStep();
2268 switch (cmd)
2270 // 2-pane view commands; target is right view
2271 case POPUPCOMMAND_USELEFTBLOCK:
2272 m_pwndRight->UseLeftBlock();
2273 break;
2274 case POPUPCOMMAND_USELEFTFILE:
2275 m_pwndRight->UseLeftFile();
2276 break;
2277 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2278 m_pwndRight->UseBothLeftFirst();
2279 break;
2280 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2281 m_pwndRight->UseBothRightFirst();
2282 break;
2283 // 3-pane view commands; target is bottom view
2284 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2285 m_pwndBottom->UseBothRightFirst();
2286 break;
2287 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2288 m_pwndBottom->UseBothLeftFirst();
2289 break;
2290 case POPUPCOMMAND_USEYOURBLOCK:
2291 m_pwndBottom->UseRightBlock();
2292 break;
2293 case POPUPCOMMAND_USEYOURFILE:
2294 m_pwndBottom->UseRightFile();
2295 break;
2296 case POPUPCOMMAND_USETHEIRBLOCK:
2297 m_pwndBottom->UseLeftBlock();
2298 break;
2299 case POPUPCOMMAND_USETHEIRFILE:
2300 m_pwndBottom->UseLeftFile();
2301 break;
2302 // copy, cut and paste commands
2303 case ID_EDIT_COPY:
2304 OnEditCopy();
2305 break;
2306 case ID_EDIT_CUT:
2307 OnEditCut();
2308 break;
2309 case ID_EDIT_PASTE:
2310 OnEditPaste();
2311 break;
2312 default:
2313 return;
2314 } // switch (cmd)
2315 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2316 return;
2319 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2321 if (!m_pViewData)
2322 return;
2324 int nViewBlockStart = -1;
2325 int nViewBlockEnd = -1;
2326 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2327 if ((point.x >= 0) && (point.y >= 0))
2329 int nLine = GetLineFromPoint(point)-1;
2330 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2332 int nViewLine = GetViewLineForScreen(nLine);
2333 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2335 ClearSelection(); // Clear text-copy selection
2337 nViewBlockStart = nViewLine;
2338 nViewBlockEnd = nViewLine;
2339 DiffStates state = m_pViewData->GetState(nViewLine);
2340 while (nViewBlockStart > 0)
2342 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2343 if (!LinesInOneChange(-1, state, lineState))
2344 break;
2345 nViewBlockStart--;
2348 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2350 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2351 if (!LinesInOneChange(1, state, lineState))
2352 break;
2353 nViewBlockEnd++;
2356 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2357 UpdateCaretPosition(point);
2362 // FixSelection(); fix selection range
2363 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2364 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2366 DiffStates state = DIFFSTATE_UNKNOWN;
2367 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2369 // find a more 'relevant' state in the selection
2370 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2372 state = m_pViewData->GetState(i);
2373 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2374 break;
2377 OnContextMenu(point, state);
2380 void CBaseView::RefreshViews()
2382 if (m_pwndLeft)
2384 m_pwndLeft->UpdateStatusBar();
2385 m_pwndLeft->Invalidate();
2387 if (m_pwndRight)
2389 m_pwndRight->UpdateStatusBar();
2390 m_pwndRight->Invalidate();
2392 if (m_pwndBottom)
2394 m_pwndBottom->UpdateStatusBar();
2395 m_pwndBottom->Invalidate();
2397 if (m_pwndLocator)
2398 m_pwndLocator->Invalidate();
2401 void CBaseView::GoToFirstDifference()
2403 SetCaretToFirstViewLine();
2404 SelectNextBlock(1, false, false);
2407 void CBaseView::GoToFirstConflict()
2409 SetCaretToFirstViewLine();
2410 SelectNextBlock(1, true, false);
2413 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2415 ClearSelection();
2416 SetupAllSelection(nStart, max(nStart, nEnd));
2418 UpdateCaretPosition(SetupPoint(0, nStart));
2419 Invalidate();
2422 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2424 ClearSelection();
2425 SetupAllViewSelection(nStart, max(nStart, nEnd));
2427 UpdateCaretViewPosition(SetupPoint(0, nStart));
2428 Invalidate();
2431 void CBaseView::SetupAllViewSelection(int start, int end)
2433 SetupViewSelection(m_pwndBottom, start, end);
2434 SetupViewSelection(m_pwndLeft, start, end);
2435 SetupViewSelection(m_pwndRight, start, end);
2438 void CBaseView::SetupAllSelection(int start, int end)
2440 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2443 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2445 void CBaseView::SetupSelection(int start, int end)
2447 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2450 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2452 if (!IsViewGood(view))
2453 return;
2454 view->SetupViewSelection(start, end);
2457 void CBaseView::SetupViewSelection(int start, int end)
2459 // clear text selection before setting line selection ?
2460 m_nSelViewBlockStart = start;
2461 m_nSelViewBlockEnd = end;
2462 Invalidate();
2466 void CBaseView::OnMergePreviousconflict()
2468 SelectNextBlock(-1, true);
2471 void CBaseView::OnMergeNextconflict()
2473 SelectNextBlock(1, true);
2476 void CBaseView::OnMergeNextdifference()
2478 SelectNextBlock(1, false);
2481 void CBaseView::OnMergePreviousdifference()
2483 SelectNextBlock(-1, false);
2486 bool CBaseView::HasNextConflict()
2488 return SelectNextBlock(1, true, true, true);
2491 bool CBaseView::HasPrevConflict()
2493 return SelectNextBlock(-1, true, true, true);
2496 bool CBaseView::HasNextDiff()
2498 return SelectNextBlock(1, false, true, true);
2501 bool CBaseView::HasPrevDiff()
2503 return SelectNextBlock(-1, false, true, true);
2506 bool CBaseView::LinesInOneChange(int direction,
2507 DiffStates initialLineState, DiffStates currentLineState)
2509 // Checks whether all the adjacent lines starting from the initial line
2510 // and up to the current line form the single change
2512 // Do not distinguish between moved and added/removed lines
2513 if (initialLineState == DIFFSTATE_MOVED_TO)
2514 initialLineState = DIFFSTATE_ADDED;
2515 if (initialLineState == DIFFSTATE_MOVED_FROM)
2516 initialLineState = DIFFSTATE_REMOVED;
2517 if (currentLineState == DIFFSTATE_MOVED_TO)
2518 currentLineState = DIFFSTATE_ADDED;
2519 if (currentLineState == DIFFSTATE_MOVED_FROM)
2520 currentLineState = DIFFSTATE_REMOVED;
2522 // First of all, if the two lines have identical states, they surely
2523 // belong to one change.
2524 if (initialLineState == currentLineState)
2525 return true;
2527 // Either we move down and initial line state is "added" or "removed" and
2528 // current line state is "empty"...
2529 if (direction > 0)
2531 if (currentLineState == DIFFSTATE_EMPTY)
2533 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2534 return true;
2536 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2537 return true;
2539 // ...or we move up and initial line state is "empty" and current line
2540 // state is "added" or "removed".
2541 if (direction < 0)
2543 if (initialLineState == DIFFSTATE_EMPTY)
2545 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2546 return true;
2548 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2549 return true;
2551 return false;
2554 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2556 if (! m_pViewData)
2557 return false;
2559 const int linesCount = (int)m_Screen2View.size();
2560 if(linesCount == 0)
2561 return false;
2563 int nCenterPos = GetCaretPosition().y;
2564 int nLimit = -1;
2565 if (nDirection > 0)
2566 nLimit = linesCount;
2568 if (nCenterPos >= linesCount)
2569 nCenterPos = linesCount-1;
2571 if (bSkipEndOfCurrentBlock)
2573 // Find end of current block
2574 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2575 while (nCenterPos != nLimit)
2577 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2578 if (!LinesInOneChange(nDirection, state, lineState))
2579 break;
2580 nCenterPos += nDirection;
2584 // Find next diff/conflict block
2585 while (nCenterPos != nLimit)
2587 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2588 if (!bConflict &&
2589 (linestate != DIFFSTATE_NORMAL) &&
2590 (linestate != DIFFSTATE_UNKNOWN))
2592 break;
2594 if (bConflict &&
2595 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2596 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2597 (linestate == DIFFSTATE_CONFLICTED) ||
2598 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2600 break;
2603 nCenterPos += nDirection;
2605 if (nCenterPos == nLimit)
2606 return false;
2607 if (dryrun)
2608 return (nCenterPos != nLimit);
2610 // Find end of new block
2611 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2612 int nBlockEnd = nCenterPos;
2613 const int maxAllowedLine = nLimit-nDirection;
2614 while (nBlockEnd != maxAllowedLine)
2616 const int lineIndex = nBlockEnd + nDirection;
2617 if (lineIndex >= linesCount)
2618 break;
2619 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2620 if (!LinesInOneChange(nDirection, state, lineState))
2621 break;
2622 nBlockEnd += nDirection;
2625 int nTopPos = nCenterPos - (GetScreenLines()/2);
2626 if (nTopPos < 0)
2627 nTopPos = 0;
2629 POINT ptCaretPos = {0, nCenterPos};
2630 SetCaretPosition(ptCaretPos);
2631 ClearSelection();
2632 if (nDirection > 0)
2633 SetupAllSelection(nCenterPos, nBlockEnd);
2634 else
2635 SetupAllSelection(nBlockEnd, nCenterPos);
2637 ScrollAllToLine(nTopPos, FALSE);
2638 RecalcAllVertScrollBars(TRUE);
2639 SetCaretToLineStart();
2640 EnsureCaretVisible();
2641 OnNavigateNextinlinediff();
2643 UpdateViewsCaretPosition();
2644 UpdateCaret();
2645 ShowDiffLines(nCenterPos);
2646 return true;
2649 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2651 if (pNMHDR->idFrom != (UINT)m_hWnd)
2652 return FALSE;
2654 CString strTipText;
2655 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2657 DWORD pos = GetMessagePos();
2658 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2659 ScreenToClient(&point);
2660 const int nLine = GetButtonEventLineIndex(point);
2662 if (nLine >= 0)
2664 int nViewLine = GetViewLineForScreen(nLine);
2665 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2667 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)
2669 strTipText.Format(IDS_MOVED_TO_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2671 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO)
2673 strTipText.Format(IDS_MOVED_FROM_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2679 *pResult = 0;
2680 if (strTipText.IsEmpty())
2681 return TRUE;
2683 // need to handle both ANSI and UNICODE versions of the message
2684 if (pNMHDR->code == TTN_NEEDTEXTA)
2686 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2687 pTTTA->lpszText = m_szTip;
2688 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2690 else
2692 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2693 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2694 pTTTW->lpszText = m_wszTip;
2697 return TRUE; // message was handled
2700 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2702 CRect rcClient;
2703 GetClientRect(rcClient);
2704 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2705 int marginwidth = MARGINWIDTH;
2706 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2708 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2710 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2712 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2714 // inside the header part of the view (showing the filename)
2715 pTI->hwnd = this->m_hWnd;
2716 this->GetClientRect(&pTI->rect);
2717 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2718 pTI->uId = (UINT)m_hWnd;
2719 pTI->lpszText = LPSTR_TEXTCALLBACK;
2721 // we want multi line tooltips
2722 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2723 if (pToolTip->GetSafeHwnd() != NULL)
2725 pToolTip->SetMaxTipWidth(INT_MAX);
2728 return (textrect.PtInRect(point) ? 1 : 2);
2731 return -1;
2734 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2736 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2737 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2739 switch (nChar)
2741 case VK_TAB:
2742 if ((nChar == '\t') && bControl)
2744 if (this==m_pwndLeft)
2746 if (IsViewGood(m_pwndRight))
2748 m_pwndRight->SetFocus();
2750 else if (IsViewGood(m_pwndBottom))
2752 m_pwndBottom->SetFocus();
2755 else if (this==m_pwndRight)
2757 if (IsViewGood(m_pwndBottom))
2759 m_pwndBottom->SetFocus();
2761 else if (IsViewGood(m_pwndLeft))
2763 m_pwndLeft->SetFocus();
2766 else if (this==m_pwndBottom)
2768 if (IsViewGood(m_pwndLeft))
2770 m_pwndLeft->SetFocus();
2772 else if (IsViewGood(m_pwndRight))
2774 m_pwndRight->SetFocus();
2778 break;
2779 case VK_PRIOR:
2781 POINT ptCaretPos = GetCaretPosition();
2782 ptCaretPos.y -= GetScreenLines();
2783 ptCaretPos.y = max(ptCaretPos.y, 0);
2784 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2785 SetCaretPosition(ptCaretPos);
2786 OnCaretMove(MOVELEFT, bShift);
2787 ShowDiffLines(ptCaretPos.y);
2789 break;
2790 case VK_NEXT:
2792 POINT ptCaretPos = GetCaretPosition();
2793 ptCaretPos.y += GetScreenLines();
2794 if (ptCaretPos.y >= GetLineCount())
2795 ptCaretPos.y = GetLineCount()-1;
2796 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2797 SetCaretPosition(ptCaretPos);
2798 OnCaretMove(MOVERIGHT, bShift);
2799 ShowDiffLines(ptCaretPos.y);
2801 break;
2802 case VK_HOME:
2804 if (bControl)
2806 ScrollAllToLine(0);
2807 SetCaretToViewStart();
2808 m_nCaretGoalPos = 0;
2809 if (bShift)
2810 AdjustSelection(MOVELEFT);
2811 else
2812 ClearSelection();
2813 UpdateCaret();
2815 else
2817 SetCaretToLineStart();
2818 m_nCaretGoalPos = 0;
2819 OnCaretMove(MOVERIGHT, bShift);
2820 ScrollAllToChar(0);
2823 break;
2824 case VK_END:
2826 if (bControl)
2828 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2829 POINT ptCaretPos;
2830 ptCaretPos.y = GetLineCount()-1;
2831 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2832 SetCaretAndGoalPosition(ptCaretPos);
2833 if (bShift)
2834 AdjustSelection(MOVERIGHT);
2835 else
2836 ClearSelection();
2838 else
2840 POINT ptCaretPos = GetCaretPosition();
2841 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2842 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2844 ptCaretPos.x--;
2846 SetCaretAndGoalPosition(ptCaretPos);
2847 OnCaretMove(bShift);
2850 break;
2851 case VK_BACK:
2852 if (IsWritable())
2854 if (! HasTextSelection())
2856 POINT ptCaretPos = GetCaretPosition();
2857 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2858 break;
2859 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2860 if (bControl)
2861 MoveCaretWordLeft();
2862 else
2864 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
2868 m_ptSelectionViewPosStart = GetCaretViewPosition();
2870 RemoveSelectedText();
2872 break;
2873 case VK_DELETE:
2874 if (IsWritable())
2876 if (! HasTextSelection())
2878 if (bControl)
2880 m_ptSelectionViewPosStart = GetCaretViewPosition();
2881 MoveCaretWordRight();
2882 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2884 else
2886 if (! MoveCaretRight())
2887 break;
2888 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2889 MoveCaretLeft();
2890 m_ptSelectionViewPosStart = GetCaretViewPosition();
2893 RemoveSelectedText();
2895 break;
2897 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2900 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2902 const int nClickedLine = GetButtonEventLineIndex(point);
2903 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2905 POINT ptCaretPos;
2906 ptCaretPos.y = nClickedLine;
2907 LONG xpos = point.x - GetMarginWidth();
2908 LONG xpos2 = xpos / GetCharWidth();
2909 xpos2 += m_nOffsetChar;
2910 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
2911 xpos2++;
2912 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
2913 SetCaretAndGoalPosition(ptCaretPos);
2915 if (nFlags & MK_SHIFT)
2916 AdjustSelection(MOVERIGHT);
2917 else
2919 ClearSelection();
2920 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
2921 if (point.x < GetMarginWidth())
2923 // select the whole line
2924 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
2925 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
2929 UpdateViewsCaretPosition();
2930 Invalidate();
2933 CView::OnLButtonDown(nFlags, point);
2936 enum ECharGroup { // ordered by priority low-to-hi
2937 CHG_UNKNOWN,
2938 CHG_CONTROL, // x00-x08, x0a-x1f
2939 CHG_WHITESPACE, // space tab
2940 CHG_PUNCTUATION, // 0x21-2f, x3a-x40, x5b-x60, x7b-x7f .,:;!?(){}[]/\<> ...
2941 CHG_WORDLETTER, // alpha num _ (others)
2944 ECharGroup GetCharGroup(wchar_t zChar)
2946 if (zChar == ' ' || zChar == '\t' )
2948 return CHG_WHITESPACE;
2950 if (zChar < 0x20)
2952 return CHG_CONTROL;
2954 if ((zChar >= 0x21 && zChar <= 0x2f)
2955 || (zChar >= 0x3a && zChar <= 0x40)
2956 || (zChar >= 0x5b && zChar <= 0x5e)
2957 || (zChar == 0x60)
2958 || (zChar >= 0x7b && zChar <= 0x7f))
2960 return CHG_PUNCTUATION;
2962 return CHG_WORDLETTER;
2965 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
2967 if (m_pViewData == 0) {
2968 CView::OnLButtonDblClk(nFlags, point);
2969 return;
2972 const int nClickedLine = GetButtonEventLineIndex(point);
2973 if ( nClickedLine < 0)
2974 return;
2975 int nViewLine = GetViewLineForScreen(nClickedLine);
2976 if (point.x < GetMarginWidth()) // only if double clicked on the margin
2978 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
2980 if((m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)||
2981 (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO))
2983 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
2984 int screenLine = FindViewLineNumber(movedindex);
2985 int nTop = screenLine - GetScreenLines()/2;
2986 if (nTop < 0)
2987 nTop = 0;
2988 ScrollAllToLine(nTop);
2989 // find and select the whole moved block
2990 int startSel = movedindex;
2991 int endSel = movedindex;
2992 while ((startSel > 0) && ((m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_TO)))
2993 startSel--;
2994 startSel++;
2995 while ((endSel < GetLineCount()) && ((m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_TO)))
2996 endSel++;
2997 endSel--;
2998 m_pOtherView->SetupSelection(startSel, endSel);
2999 return CView::OnLButtonDblClk(nFlags, point);
3003 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3005 // a double click on a marker expands the hidden text
3006 int i = nViewLine;
3007 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3009 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3010 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3011 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3012 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3013 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3014 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3015 i++;
3017 BuildAllScreen2ViewVector();
3018 if (m_pwndLeft)
3019 m_pwndLeft->Invalidate();
3020 if (m_pwndRight)
3021 m_pwndRight->Invalidate();
3022 if (m_pwndBottom)
3023 m_pwndBottom->Invalidate();
3025 else
3027 POINT ptCaretPos;
3028 ptCaretPos.y = nClickedLine;
3029 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3030 SetCaretPosition(ptCaretPos);
3031 ClearSelection();
3033 POINT ptViewCarret = GetCaretViewPosition();
3034 nViewLine = ptViewCarret.y;
3035 if (nViewLine >= GetViewCount())
3036 return;
3037 CString sLine = GetViewLine(nViewLine);
3038 int nLineLength = sLine.GetLength();
3039 int nBasePos = ptViewCarret.x;
3040 // get target char group
3041 ECharGroup eLeft = CHG_UNKNOWN;
3042 if (nBasePos > 0)
3044 eLeft = GetCharGroup(sLine[nBasePos-1]);
3046 ECharGroup eRight = CHG_UNKNOWN;
3047 if (nBasePos < nLineLength)
3049 eRight = GetCharGroup(sLine[nBasePos]);
3051 ECharGroup eTarget = max(eRight, eLeft);
3052 // find left margin
3053 int nLeft = nBasePos;
3054 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3056 nLeft--;
3058 // get right margin
3059 int nRight = nBasePos;
3060 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3062 nRight++;
3064 // set selection
3065 m_ptSelectionViewPosStart.x = nLeft;
3066 m_ptSelectionViewPosStart.y = nViewLine;
3067 m_ptSelectionViewPosEnd.x = nRight;
3068 m_ptSelectionViewPosEnd.y = nViewLine;
3069 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3070 SetupAllViewSelection(nViewLine, nViewLine);
3071 // set caret
3072 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3073 UpdateViewsCaretPosition();
3074 UpdateGoalPos();
3076 // set mark word
3077 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3078 int nMarkWidth = max(nRight - nLeft, 0);
3079 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3080 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3082 m_sMarkedWord.Empty();
3085 if (m_pwndLeft)
3086 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3087 if (m_pwndRight)
3088 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3089 if (m_pwndBottom)
3090 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3092 Invalidate();
3093 if (m_pwndLocator)
3094 m_pwndLocator->Invalidate();
3097 CView::OnLButtonDblClk(nFlags, point);
3100 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3102 const int nClickedLine = GetButtonEventLineIndex(point);
3103 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3105 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3107 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3108 if (pidl)
3110 SHOpenFolderAndSelectItems(pidl,0,0,0);
3111 CoTaskMemFree((LPVOID)pidl);
3114 return;
3116 POINT ptCaretPos;
3117 ptCaretPos.y = nClickedLine;
3118 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3119 SetCaretAndGoalPosition(ptCaretPos);
3120 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3121 if (m_pwndLeft)
3122 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3123 if (m_pwndRight)
3124 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3125 if (m_pwndBottom)
3126 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3127 ClearSelection();
3128 m_ptSelectionViewPosStart.x = 0;
3129 m_ptSelectionViewPosStart.y = nClickedLine;
3130 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3131 m_ptSelectionViewPosEnd.y = nClickedLine;
3132 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3133 UpdateViewsCaretPosition();
3134 Invalidate();
3135 if (m_pwndLocator)
3136 m_pwndLocator->Invalidate();
3139 void CBaseView::OnEditCopy()
3141 CString sCopyData = GetSelectedText();
3143 if (!sCopyData.IsEmpty())
3145 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3149 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3151 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3153 --m_pMainFrame->m_nMoveMovesToIgnore;
3154 CView::OnMouseMove(nFlags, point);
3155 return;
3157 int nMouseLine = GetButtonEventLineIndex(point);
3158 if (nMouseLine < -1)
3159 nMouseLine = -1;
3160 m_mouseInMargin = point.x < GetMarginWidth();
3162 ShowDiffLines(nMouseLine);
3164 KillTimer(IDT_SCROLLTIMER);
3165 if (nFlags & MK_LBUTTON)
3167 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3168 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3169 if (saveMouseLine < 0)
3170 return;
3171 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3172 if (HasSelection() &&
3173 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3175 POINT ptCaretPos = {charIndex, nMouseLine};
3176 SetCaretAndGoalPosition(ptCaretPos);
3177 AdjustSelection(MOVERIGHT);
3178 Invalidate();
3179 UpdateWindow();
3181 if (nMouseLine < m_nTopLine)
3183 ScrollAllToLine(m_nTopLine-1, TRUE);
3184 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3186 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3188 ScrollAllToLine(m_nTopLine+1, TRUE);
3189 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3191 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3193 ScrollAllSide(-1);
3194 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3196 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3198 ScrollAllSide(1);
3199 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3201 SetCapture();
3205 CView::OnMouseMove(nFlags, point);
3208 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3210 ShowDiffLines(-1);
3211 ReleaseCapture();
3212 KillTimer(IDT_SCROLLTIMER);
3214 __super::OnLButtonUp(nFlags, point);
3217 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3219 if (nIDEvent == IDT_SCROLLTIMER)
3221 POINT point;
3222 GetCursorPos(&point);
3223 ScreenToClient(&point);
3224 int nMouseLine = GetButtonEventLineIndex(point);
3225 if (nMouseLine < -1)
3227 nMouseLine = -1;
3229 if (GetKeyState(VK_LBUTTON)&0x8000)
3231 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3232 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3233 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3234 if (nMouseLine < m_nTopLine)
3236 ScrollAllToLine(m_nTopLine-1, TRUE);
3237 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3239 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3241 ScrollAllToLine(m_nTopLine+1, TRUE);
3242 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3244 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3246 ScrollAllSide(-1);
3247 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3249 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3251 ScrollAllSide(1);
3252 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3258 CView::OnTimer(nIDEvent);
3261 void CBaseView::ShowDiffLines(int nLine)
3263 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3265 m_pwndLineDiffBar->ShowLines(nLine);
3266 nLine = -1;
3267 m_nMouseLine = nLine;
3268 return;
3271 if ((!m_pwndRight)||(!m_pwndLeft))
3272 return;
3273 if(m_pMainFrame->m_bOneWay)
3274 return;
3276 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3277 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3279 if (nLine < 0)
3280 return;
3282 if (nLine != m_nMouseLine)
3284 if (nLine >= GetLineCount())
3285 nLine = -1;
3286 m_nMouseLine = nLine;
3287 m_pwndLineDiffBar->ShowLines(nLine);
3289 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3292 const viewdata& CBaseView::GetEmptyLineData()
3294 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN, -1);
3295 return emptyLine;
3298 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3300 for (int i = 0; i < nCount; i++)
3302 InsertViewData(nFirstView, GetEmptyLineData());
3307 void CBaseView::UpdateCaret()
3309 POINT ptCaretPos = GetCaretPosition();
3310 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3311 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3312 SetCaretPosition(ptCaretPos);
3314 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3316 if (m_bFocused &&
3317 ptCaretPos.y >= m_nTopLine &&
3318 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3319 nCaretOffset >= m_nOffsetChar &&
3320 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3322 CreateSolidCaret(2, GetLineHeight());
3323 SetCaretPos(TextToClient(ptCaretPos));
3324 ShowCaret();
3326 else
3328 HideCaret();
3332 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3334 POINT ptViewPos;
3335 ptViewPos.x = pt.x;
3337 int nSubLine = GetSubLineOffset(pt.y);
3338 if (nSubLine > 0)
3340 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3342 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3346 ptViewPos.y = GetViewLineForScreen(pt.y);
3347 return ptViewPos;
3350 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3352 POINT ptPos;
3353 int nViewLineLenLeft = GetViewLineLength(pt.y);
3354 ptPos.x = min(nViewLineLenLeft, pt.x);
3355 ptPos.y = FindScreenLineForViewLine(pt.y);
3356 if (GetViewLineForScreen(ptPos.y) != pt.y )
3358 ptPos.x = 0;
3360 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3362 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3363 while (nSubLineLength < ptPos.x)
3365 ptPos.x -= nSubLineLength;
3366 nViewLineLenLeft -= nSubLineLength;
3367 ptPos.y++;
3368 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3370 // last pos of non last sub-line go to start of next screen line
3371 // Note: while this works correctly, it's not what a user might expect:
3372 // cursor-right when the caret is before the last char of a wrapped line
3373 // now moves the caret to the next line. But users expect the caret to
3374 // move to the right of the last char instead, and with another cursor-right
3375 // keystroke to move the caret to the next line.
3376 // Basically, this would require to handle two caret positions for the same
3377 // logical position in the line string (one on the last position of the first line,
3378 // one on the first position of the new line. For non-wrapped lines this works
3379 // because there's an 'invisible' newline char at the end of the first line.
3380 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3382 ptPos.x = 0;
3383 ptPos.y++;
3387 return ptPos;
3391 void CBaseView::EnsureCaretVisible()
3393 POINT ptCaretPos = GetCaretPosition();
3394 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3396 if (ptCaretPos.y < m_nTopLine)
3397 ScrollAllToLine(ptCaretPos.y);
3398 int screnLines = GetScreenLines();
3399 if (screnLines)
3401 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3402 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3403 if (nCaretOffset < m_nOffsetChar)
3404 ScrollAllToChar(nCaretOffset);
3405 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3406 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3410 int CBaseView::CalculateActualOffset(const POINT& point)
3412 int nLineIndex = point.y;
3413 int nCharIndex = point.x;
3414 ASSERT(nCharIndex >= 0);
3415 CString sLine = GetLineChars(nLineIndex);
3416 int nLineLength = sLine.GetLength();
3417 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3420 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3422 int nLength = GetLineLength(nLineIndex);
3423 int nSubLine = GetSubLineOffset(nLineIndex);
3424 if (nSubLine>=0)
3426 int nViewLine = GetViewLineForScreen(nLineIndex);
3427 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3429 int nMultilineCount = CountMultiLines(nViewLine);
3430 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3432 nLength--;
3436 CString Line = GetLineChars(nLineIndex);
3437 int nIndex = 0;
3438 int nOffset = 0;
3439 int nTabSize = GetTabSize();
3440 while (nOffset < nActualOffset && nIndex < nLength)
3442 if (Line.GetAt(nIndex) == _T('\t'))
3443 nOffset += (nTabSize - nOffset % nTabSize);
3444 else
3445 ++nOffset;
3446 ++nIndex;
3448 return nIndex;
3451 POINT CBaseView::TextToClient(const POINT& point)
3453 POINT pt;
3454 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3455 pt.y = nOffsetScreenLine * GetLineHeight();
3456 pt.x = CalculateActualOffset(point);
3458 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3459 CDC * pDC = GetDC();
3460 if (pDC)
3462 pDC->SelectObject(GetFont()); // is this right font ?
3463 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3464 CString sLine = GetLineChars(nScreenLine);
3465 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3466 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3467 ReleaseDC(pDC);
3468 } else {
3469 nLeft += pt.x * GetCharWidth();
3472 pt.x = nLeft;
3473 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3474 return pt;
3477 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3479 CView::OnChar(nChar, nRepCnt, nFlags);
3481 if (IsReadonly())
3482 return;
3484 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3485 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3487 return;
3490 if (!m_pViewData) // no data - nothing to do
3491 return;
3493 if (nChar == VK_F16)
3495 // generated by a ctrl+backspace - ignore.
3497 else if ((nChar > 31)||(nChar == VK_TAB))
3499 ResetUndoStep();
3500 RemoveSelectedText();
3501 POINT ptCaretViewPos = GetCaretViewPosition();
3502 int nViewLine = ptCaretViewPos.y;
3503 if ((nViewLine==0)&&(GetViewCount()==0))
3504 OnChar(VK_RETURN, 0, 0);
3505 viewdata lineData = GetViewData(nViewLine);
3506 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3507 if (IsStateEmpty(lineData.state))
3509 // if not last line set EOL
3510 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3512 if (!IsViewLineEmpty(nCheckViewLine))
3514 lineData.ending = lineendings;
3515 break;
3518 // make sure previous (non empty) line have EOL set
3519 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3521 if (!IsViewLineEmpty(nCheckViewLine))
3523 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3525 SetViewLineEnding(nCheckViewLine, lineendings);
3527 break;
3531 lineData.state = DIFFSTATE_EDITED;
3532 bool bNeedRenumber = false;
3533 if (lineData.linenumber == -1)
3535 lineData.linenumber = 0;
3536 bNeedRenumber = true;
3538 SetViewData(nViewLine, lineData);
3539 SaveUndoStep();
3540 BuildAllScreen2ViewVector(nViewLine);
3541 if (bNeedRenumber)
3543 UpdateViewLineNumbers();
3545 MoveCaretRight();
3546 UpdateGoalPos();
3548 else if (nChar == 10)
3550 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3551 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3552 EOL newEOL = EOL_CRLF;
3553 switch (eol)
3555 case EOL_CRLF:
3556 newEOL = EOL_CR;
3557 break;
3558 case EOL_CR:
3559 newEOL = EOL_LF;
3560 break;
3561 case EOL_LF:
3562 newEOL = EOL_CRLF;
3563 break;
3565 if (eol==EOL_NOENDING || eol==newEOL)
3566 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3567 // to add EOL on newly edited empty line hit enter
3568 // don't store into UNDO if no change happened
3569 // and don't mark file as modified
3570 return;
3571 AddUndoViewLine(nViewLine);
3572 m_pViewData->SetLineEnding(nViewLine, newEOL);
3573 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3574 UpdateGoalPos();
3576 else if (nChar == VK_RETURN)
3578 // insert a new, fresh and empty line below the cursor
3579 RemoveSelectedText();
3581 CUndo::GetInstance().BeginGrouping();
3583 POINT ptCaretViewPos = GetCaretViewPosition();
3584 int nViewLine = ptCaretViewPos.y;
3585 int nLeft = ptCaretViewPos.x;
3586 CString sLine = GetViewLineChars(nViewLine);
3587 CString sLineLeft = sLine.Left(nLeft);
3588 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3589 EOL eOriginalEnding = EOL_AUTOLINE;
3590 if (m_pViewData->GetCount() > nViewLine)
3591 eOriginalEnding = GetViewLineEnding(nViewLine);
3593 if (!sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3595 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3596 SetViewData(nViewLine, newFirstLine);
3599 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3600 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN, -1);
3601 InsertViewData(nInsertLine, newLastLine);
3602 SaveUndoStep();
3604 // adds new line everywhere except me
3605 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3607 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3609 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3611 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3613 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3615 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3617 SaveUndoStep();
3619 UpdateViewLineNumbers();
3620 SaveUndoStep();
3621 CUndo::GetInstance().EndGrouping();
3623 BuildAllScreen2ViewVector();
3624 // move the cursor to the new line
3625 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3626 SetCaretAndGoalViewPosition(ptCaretViewPos);
3628 else
3629 return; // Unknown control character -- ignore it.
3630 ClearSelection();
3631 EnsureCaretVisible();
3632 UpdateCaret();
3633 SetModified(true);
3634 Invalidate(FALSE);
3637 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3639 ResetUndoStep();
3640 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3641 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3642 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3643 SaveUndoStep();
3644 RecalcAllVertScrollBars();
3645 Invalidate(FALSE);
3648 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3650 if (m_pViewData == NULL)
3651 return;
3652 int viewLine = nViewLineIndex;
3653 EOL ending = m_pViewData->GetLineEnding(viewLine);
3654 if (ending == EOL_NOENDING)
3656 ending = lineendings;
3658 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN, -1);
3659 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3661 CString sPartLine = GetViewLineChars(nViewLineIndex);
3662 int nPosx = GetCaretPosition().x; // should be view pos ?
3663 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3664 sPartLine = sPartLine.Mid(nPosx);
3665 newLine.sLine = sPartLine;
3667 m_pViewData->InsertData(viewLine+1, newLine);
3668 BuildAllScreen2ViewVector();
3671 void CBaseView::RemoveSelectedText()
3673 if (m_pViewData == NULL)
3674 return;
3675 if (!HasTextSelection())
3676 return;
3678 // fix selection if starts or ends on empty line
3679 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3680 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3683 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3684 SetCaretViewPosition(m_ptSelectionViewPosStart);
3685 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3688 m_ptSelectionViewPosStart = GetCaretViewPosition();
3689 if (!HasTextSelection())
3691 ClearSelection();
3692 return;
3695 // We want to undo the insertion in a single step.
3696 ResetUndoStep();
3697 CUndo::GetInstance().BeginGrouping();
3699 // combine first and last line
3700 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3701 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3702 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3703 oFirstLine.ending = oLastLine.ending;
3704 oFirstLine.state = DIFFSTATE_EDITED;
3705 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3707 // clean up middle lines if any
3708 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3710 viewdata oEmptyLine = GetEmptyLineData();
3711 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3713 SetViewData(nViewLine, oEmptyLine);
3715 SaveUndoStep();
3717 if (CleanEmptyLines())
3719 BuildAllScreen2ViewVector(); // schedule full rebuild
3721 SaveUndoStep();
3722 UpdateViewLineNumbers();
3725 SaveUndoStep();
3726 CUndo::GetInstance().EndGrouping();
3728 SetModified();
3729 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3730 SetCaretViewPosition(m_ptSelectionViewPosStart);
3731 UpdateGoalPos();
3732 ClearSelection();
3733 UpdateCaret();
3734 EnsureCaretVisible();
3735 Invalidate(FALSE);
3738 void CBaseView::PasteText()
3740 if (!OpenClipboard())
3741 return;
3743 CString sClipboardText;
3744 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3745 if (hglb)
3747 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3748 sClipboardText = CString(lpstr);
3749 GlobalUnlock(hglb);
3751 hglb = GetClipboardData(CF_UNICODETEXT);
3752 if (hglb)
3754 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3755 sClipboardText = lpstr;
3756 GlobalUnlock(hglb);
3758 CloseClipboard();
3760 if (sClipboardText.IsEmpty())
3761 return;
3763 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3764 sClipboardText.Replace('\n', '\r');
3766 ResetUndoStep();
3768 POINT ptCaretViewPos = GetCaretViewPosition();
3769 int nLeft = ptCaretViewPos.x;
3770 int nViewLine = ptCaretViewPos.y;
3772 if ((nViewLine==0)&&(GetViewCount()==0))
3773 OnChar(VK_RETURN, 0, 0);
3775 std::vector<CString> lines;
3776 int nStart = 0;
3777 int nEolPos = 0;
3778 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3780 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3781 lines.push_back(sLine);
3782 nEolPos++;
3783 nStart = nEolPos;
3785 CString sLine = sClipboardText.Mid(nStart);
3786 lines.push_back(sLine);
3788 int nLinesToPaste = (int)lines.size();
3789 if (nLinesToPaste > 1)
3791 // multiline text
3793 // We want to undo the multiline insertion in a single step.
3794 CUndo::GetInstance().BeginGrouping();
3796 sLine = GetViewLineChars(nViewLine);
3797 CString sLineLeft = sLine.Left(nLeft);
3798 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3799 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3800 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3801 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3803 newLine.sLine = sLineLeft + lines[0];
3804 SetViewData(nViewLine, newLine);
3807 int nInsertLine = nViewLine;
3808 for (int i = 1; i < nLinesToPaste-1; i++)
3810 newLine.sLine = lines[i];
3811 InsertViewData(++nInsertLine, newLine);
3813 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3814 newLine.ending = eOriginalEnding;
3815 InsertViewData(++nInsertLine, newLine);
3817 SaveUndoStep();
3819 // adds new lines everywhere except me
3820 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3822 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3824 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3826 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3828 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3830 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3832 SaveUndoStep();
3834 UpdateViewLineNumbers();
3835 CUndo::GetInstance().EndGrouping();
3837 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3839 else
3841 // single line text - just insert it
3842 sLine = GetViewLineChars(nViewLine);
3843 sLine.Insert(nLeft, sClipboardText);
3844 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
3845 SetViewLine(nViewLine, sLine);
3846 SetViewState(nViewLine, DIFFSTATE_EDITED);
3847 SaveUndoStep();
3850 SetModified();
3851 RefreshViews();
3852 BuildAllScreen2ViewVector();
3853 UpdateCaretViewPosition(ptCaretViewPos);
3856 void CBaseView::OnCaretDown()
3858 POINT ptCaretPos = GetCaretPosition();
3859 int nLine = ptCaretPos.y;
3860 int nNextLine = nLine + 1;
3861 if (nNextLine >= GetLineCount()) // already at last line
3863 return;
3866 POINT ptCaretViewPos = GetCaretViewPosition();
3867 int nViewLine = ptCaretViewPos.y;
3868 int nNextViewLine = GetViewLineForScreen(nNextLine);
3869 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3871 // find next suitable screen line
3872 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3874 nNextLine++;
3875 if (nNextLine >= GetLineCount())
3877 return;
3879 nNextViewLine = GetViewLineForScreen(nNextLine);
3882 ptCaretPos.y = nNextLine;
3883 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3884 SetCaretPosition(ptCaretPos);
3885 OnCaretMove(MOVELEFT);
3886 ShowDiffLines(ptCaretPos.y);
3889 bool CBaseView::MoveCaretLeft()
3891 POINT ptCaretViewPos = GetCaretViewPosition();
3893 //int nViewLine = ptCaretViewPos.y;
3894 if (ptCaretViewPos.x == 0)
3896 int nPrevLine = GetCaretPosition().y;
3897 int nPrevViewLine;
3898 do {
3899 nPrevLine--;
3900 if (nPrevLine < 0)
3902 return false;
3904 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3905 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
3906 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
3907 ShowDiffLines(nPrevLine);
3909 else
3910 --ptCaretViewPos.x;
3912 SetCaretAndGoalViewPosition(ptCaretViewPos);
3913 return true;
3916 bool CBaseView::MoveCaretRight()
3918 POINT ptCaretViewPos = GetCaretViewPosition();
3920 int nViewLine = ptCaretViewPos.y;
3921 int nViewLineLen = GetViewLineLength(nViewLine);
3922 if (ptCaretViewPos.x >= nViewLineLen)
3924 int nNextLine = GetCaretPosition().y;
3925 int nNextViewLine;
3926 do {
3927 nNextLine++;
3928 if (nNextLine >= GetLineCount())
3930 return false;
3932 nNextViewLine = GetViewLineForScreen(nNextLine);
3933 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
3934 ptCaretViewPos.y = nNextViewLine;
3935 ptCaretViewPos.x = 0;
3936 ShowDiffLines(nNextLine);
3938 else
3939 ++ptCaretViewPos.x;
3941 SetCaretAndGoalViewPosition(ptCaretViewPos);
3942 return true;
3945 void CBaseView::UpdateGoalPos()
3947 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
3950 void CBaseView::OnCaretLeft()
3952 MoveCaretLeft();
3953 OnCaretMove(MOVELEFT);
3956 void CBaseView::OnCaretRight()
3958 MoveCaretRight();
3959 OnCaretMove(MOVERIGHT);
3962 void CBaseView::OnCaretUp()
3964 POINT ptCaretPos = GetCaretPosition();
3965 int nLine = ptCaretPos.y;
3966 if (nLine <= 0) // already at first line
3968 return;
3970 int nPrevLine = nLine - 1;
3972 POINT ptCaretViewPos = GetCaretViewPosition();
3973 int nViewLine = ptCaretViewPos.y;
3974 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
3975 if (nPrevViewLine != nViewLine) // not on same view line
3977 // find previous suitable screen line
3978 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
3980 if (nPrevLine <= 0)
3982 return;
3984 nPrevLine--;
3985 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3988 ptCaretPos.y = nPrevLine;
3989 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3990 SetCaretPosition(ptCaretPos);
3991 OnCaretMove(MOVELEFT);
3992 ShowDiffLines(ptCaretPos.y);
3995 bool CBaseView::IsWordSeparator(const wchar_t ch) const
3997 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
4000 bool CBaseView::IsCaretAtWordBoundary()
4002 POINT ptViewCaret = GetCaretViewPosition();
4003 CString line = GetViewLineChars(ptViewCaret.y);
4004 if (line.IsEmpty())
4005 return false; // no boundary at the empty lines
4006 if (ptViewCaret.x == 0)
4007 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4008 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4009 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4010 return
4011 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4012 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4015 void CBaseView::UpdateViewsCaretPosition()
4017 POINT ptCaretPos = GetCaretPosition();
4018 if (m_pwndBottom && m_pwndBottom!=this)
4019 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4020 if (m_pwndLeft && m_pwndLeft!=this)
4021 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4022 if (m_pwndRight && m_pwndRight!=this)
4023 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4026 void CBaseView::OnCaretWordleft()
4028 MoveCaretWordLeft();
4029 OnCaretMove(MOVELEFT);
4032 void CBaseView::OnCaretWordright()
4034 MoveCaretWordRight();
4035 OnCaretMove(MOVERIGHT);
4038 void CBaseView::MoveCaretWordLeft()
4040 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4045 void CBaseView::MoveCaretWordRight()
4047 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4052 void CBaseView::ClearCurrentSelection()
4054 m_ptSelectionViewPosStart = GetCaretViewPosition();
4055 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4056 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4057 m_nSelViewBlockStart = -1;
4058 m_nSelViewBlockEnd = -1;
4059 Invalidate(FALSE);
4062 void CBaseView::ClearSelection()
4064 if (m_pwndLeft)
4065 m_pwndLeft->ClearCurrentSelection();
4066 if (m_pwndRight)
4067 m_pwndRight->ClearCurrentSelection();
4068 if (m_pwndBottom)
4069 m_pwndBottom->ClearCurrentSelection();
4072 void CBaseView::AdjustSelection(bool bMoveLeft)
4074 POINT ptCaretViewPos = GetCaretViewPosition();
4075 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4077 // select all have been used recently update origin
4078 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4080 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4081 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4083 m_ptSelectionViewPosStart = ptCaretViewPos;
4084 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4086 else
4088 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4089 m_ptSelectionViewPosEnd = ptCaretViewPos;
4092 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4094 Invalidate(FALSE);
4097 void CBaseView::OnEditCut()
4099 if (IsWritable())
4101 OnEditCopy();
4102 RemoveSelectedText();
4106 void CBaseView::OnEditPaste()
4108 if (IsWritable())
4110 CUndo::GetInstance().BeginGrouping();
4111 RemoveSelectedText();
4112 PasteText();
4113 CUndo::GetInstance().EndGrouping();
4117 void CBaseView::DeleteFonts()
4119 for (int i=0; i<fontsCount; i++)
4121 if (m_apFonts[i] != NULL)
4123 m_apFonts[i]->DeleteObject();
4124 delete m_apFonts[i];
4125 m_apFonts[i] = NULL;
4130 void CBaseView::OnCaretMove(bool bMoveLeft)
4132 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4133 OnCaretMove(bMoveLeft, bShift);
4136 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4138 if(isShiftPressed)
4139 AdjustSelection(bMoveLeft);
4140 else
4141 ClearSelection();
4142 EnsureCaretVisible();
4143 UpdateCaret();
4146 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4148 AddCutCopyAndPaste(popup);
4151 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4153 popup.AppendMenu(MF_SEPARATOR, NULL);
4154 CString temp;
4155 temp.LoadString(IDS_EDIT_COPY);
4156 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4157 if (IsWritable())
4159 temp.LoadString(IDS_EDIT_CUT);
4160 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4161 temp.LoadString(IDS_EDIT_PASTE);
4162 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4166 void CBaseView::CompensateForKeyboard(CPoint& point)
4168 // if the context menu is invoked through the keyboard, we have to use
4169 // a calculated position on where to anchor the menu on
4170 if (ArePointsSame(point, SetupPoint(-1, -1)))
4172 CRect rect;
4173 GetWindowRect(&rect);
4174 point = rect.CenterPoint();
4178 HICON CBaseView::LoadIcon(WORD iconId)
4180 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4181 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4182 return (HICON)icon;
4185 void CBaseView::ReleaseBitmap()
4187 if (m_pCacheBitmap != NULL)
4189 m_pCacheBitmap->DeleteObject();
4190 delete m_pCacheBitmap;
4191 m_pCacheBitmap = NULL;
4195 void CBaseView::BuildMarkedWordArray()
4197 int lineCount = GetLineCount();
4198 m_arMarkedWordLines.clear();
4199 m_arMarkedWordLines.reserve(lineCount);
4200 bool bDoit = !m_sMarkedWord.IsEmpty();
4201 for (int i = 0; i < lineCount; ++i)
4203 if (bDoit)
4205 CString line = GetLineChars(i);
4207 if (!line.IsEmpty())
4209 m_arMarkedWordLines.push_back(line.Find(m_sMarkedWord) != -1);
4211 else
4212 m_arMarkedWordLines.push_back(0);
4214 else
4215 m_arMarkedWordLines.push_back(0);
4219 void CBaseView::BuildFindStringArray()
4221 int lineCount = GetLineCount();
4222 m_arFindStringLines.clear();
4223 m_arFindStringLines.reserve(lineCount);
4224 bool bDoit = !m_sFindText.IsEmpty();
4225 int s = 0;
4226 int e = 0;
4227 for (int i = 0; i < lineCount; ++i)
4229 if (bDoit)
4231 CString line = GetLineChars(i);
4233 if (!line.IsEmpty())
4235 line = line.MakeLower();
4236 m_arFindStringLines.push_back(StringFound(line, SearchNext, s, e));
4238 else
4239 m_arFindStringLines.push_back(0);
4241 else
4242 m_arFindStringLines.push_back(0);
4244 UpdateLocator();
4247 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4249 if (!m_bShowInlineDiff)
4250 return false;
4251 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4252 return false;
4254 CString sLine = GetViewLineChars(nViewLine);
4255 if (sLine.IsEmpty())
4256 return false;
4258 CheckOtherView();
4259 if (!m_pOtherViewData)
4260 return false;
4262 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4263 if (sDiffLine.IsEmpty())
4264 return false;
4266 CString sLineExp = ExpandChars(sLine);
4267 CString sDiffLineExp = ExpandChars(sDiffLine);
4268 svn_diff_t * diff = NULL;
4269 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4270 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4271 return false;
4273 size_t lineoffset = 0;
4274 size_t position = 0;
4275 while (diff)
4277 apr_off_t len = diff->original_length;
4278 size_t oldpos = position;
4280 for (apr_off_t i = 0; i < len; ++i)
4282 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4283 lineoffset++;
4286 if (diff->type == svn_diff__type_diff_modified)
4288 inlineDiffPos p;
4289 p.start = oldpos;
4290 p.end = position;
4291 positions.push_back(p);
4294 diff = diff->next;
4297 return !positions.empty();
4300 void CBaseView::OnNavigateNextinlinediff()
4302 int nX;
4303 if (GetNextInlineDiff(nX))
4305 POINT ptCaretViewPos = GetCaretViewPosition();
4306 ptCaretViewPos.x = nX;
4307 SetCaretAndGoalViewPosition(ptCaretViewPos);
4308 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4309 EnsureCaretVisible();
4313 void CBaseView::OnNavigatePrevinlinediff()
4315 int nX;
4316 if (GetPrevInlineDiff(nX))
4318 POINT ptCaretViewPos = GetCaretViewPosition();
4319 ptCaretViewPos.x = nX;
4320 SetCaretAndGoalViewPosition(ptCaretViewPos);
4321 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4322 EnsureCaretVisible();
4326 bool CBaseView::HasNextInlineDiff()
4328 int nPos;
4329 return GetNextInlineDiff(nPos);
4332 bool CBaseView::GetNextInlineDiff(int & nPos)
4334 POINT ptCaretViewPos = GetCaretViewPosition();
4335 std::vector<inlineDiffPos> positions;
4336 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4338 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4340 if (it->start > ptCaretViewPos.x)
4342 nPos = (LONG)it->start;
4343 return true;
4345 if (it->end > ptCaretViewPos.x)
4347 nPos = (LONG)it->end;
4348 return true;
4352 return false;
4355 bool CBaseView::HasPrevInlineDiff()
4357 int nPos;
4358 return GetPrevInlineDiff(nPos);
4361 bool CBaseView::GetPrevInlineDiff(int & nPos)
4363 POINT ptCaretViewPos = GetCaretViewPosition();
4364 std::vector<inlineDiffPos> positions;
4365 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4367 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4369 if ( it->end < ptCaretViewPos.x)
4371 nPos = (LONG)it->end;
4372 return true;
4374 if ( it->start < ptCaretViewPos.x)
4376 nPos = (LONG)it->start;
4377 return true;
4381 return false;
4384 CBaseView * CBaseView::GetFirstGoodView()
4386 if (IsViewGood(m_pwndLeft))
4387 return m_pwndLeft;
4388 if (IsViewGood(m_pwndRight))
4389 return m_pwndRight;
4390 if (IsViewGood(m_pwndBottom))
4391 return m_pwndBottom;
4392 return NULL;
4395 void CBaseView::BuildAllScreen2ViewVector()
4397 CBaseView * p_pwndView = GetFirstGoodView();
4398 if (p_pwndView)
4400 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4404 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4406 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4409 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4411 CBaseView * p_pwndView = GetFirstGoodView();
4412 if (p_pwndView)
4414 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4418 void CBaseView::UpdateViewLineNumbers()
4420 int nLineNumber = 0;
4421 int nViewLineCount = GetViewCount();
4422 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4424 int oldLine = (int)GetViewLineNumber(nViewLine);
4425 if (oldLine >= 0)
4426 SetViewLineNumber(nViewLine, nLineNumber++);
4428 m_nDigits = 0;
4431 int CBaseView::CleanEmptyLines()
4433 int nRemovedCount = 0;
4434 int nViewLineCount = GetViewCount();
4435 bool bCheckLeft = IsViewGood(m_pwndLeft);
4436 bool bCheckRight = IsViewGood(m_pwndRight);
4437 bool bCheckBottom = IsViewGood(m_pwndBottom);
4438 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4440 bool bAllEmpty = true;
4441 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4442 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4443 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4444 if (bAllEmpty)
4446 if (bCheckLeft)
4448 m_pwndLeft->RemoveViewData(nViewLine);
4450 if (bCheckRight)
4452 m_pwndRight->RemoveViewData(nViewLine);
4454 if (bCheckBottom)
4456 m_pwndBottom->RemoveViewData(nViewLine);
4458 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4460 SaveUndoStep();
4462 nViewLineCount--;
4463 nRemovedCount++;
4464 continue;
4466 nViewLine++;
4468 return nRemovedCount;
4471 int CBaseView::FindScreenLineForViewLine( int viewLine )
4473 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4476 int CBaseView::CountMultiLines( int nViewLine )
4478 if (m_ScreenedViewLine.empty())
4479 return 0; // in case the view is completely empty
4481 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4483 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4485 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4488 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4490 TScreenedViewLine oScreenedLine;
4491 // tokenize string
4492 int prevpos = 0;
4493 int pos = 0;
4494 while ((pos = multiline.Find('\n', pos)) >= 0)
4496 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4497 pos++;
4498 prevpos = pos;
4500 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4501 oScreenedLine.bSublinesSet = true;
4502 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4504 return CountMultiLines(nViewLine);
4507 /// prepare inline diff cache
4508 LineColors & CBaseView::GetLineColors(int nViewLine)
4510 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4512 if (m_bWhitespaceInlineDiffs)
4514 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4515 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4517 else
4519 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4520 return m_ScreenedViewLine[nViewLine].lineColors;
4523 LineColors oLineColors;
4524 // set main line color
4525 COLORREF crBkgnd, crText;
4526 DiffStates diffState = m_pViewData->GetState(nViewLine);
4527 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4528 oLineColors.SetColor(0, crText, crBkgnd);
4530 do {
4531 if (!m_bShowInlineDiff)
4532 break;
4534 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4535 break;
4537 CString sLine = GetViewLineChars(nViewLine);
4538 if (sLine.IsEmpty())
4539 break;
4540 if (!m_pOtherView)
4541 break;
4543 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4544 if (sDiffLine.IsEmpty())
4545 break;
4547 svn_diff_t * diff = NULL;
4548 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4549 break;
4550 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4551 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4552 break;
4554 int lineoffset = 0;
4555 int nTextStartOffset = 0;
4556 std::map<int, COLORREF> removedPositions;
4557 while (diff)
4559 apr_off_t len = diff->original_length;
4561 CString s;
4562 for (int i = 0; i < len; ++i)
4564 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4565 lineoffset++;
4567 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4568 int nTextLength = s.GetLength();
4570 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4571 if ((m_bShowInlineDiff)&&(bInlineDiff))
4573 crBkgnd = InlineViewLineDiffColor(nViewLine);
4575 else
4577 crBkgnd = m_ModifiedBk;
4580 if (len < diff->modified_length)
4582 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4584 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4586 nTextStartOffset += nTextLength;
4587 diff = diff->next;
4589 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4591 oLineColors.AddShotColor(it->first, it->second);
4593 } while (false); // error catch
4595 if (!m_bWhitespaceInlineDiffs)
4597 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4598 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4600 else
4602 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4603 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4606 return GetLineColors(nViewLine);
4609 void CBaseView::OnEditSelectall()
4611 if (m_pViewData == nullptr)
4612 return;
4613 int nLastViewLine = m_pViewData->GetCount()-1;
4614 if (nLastViewLine < 0)
4615 return;
4616 SetupAllViewSelection(0, nLastViewLine);
4618 CString sLine = GetViewLineChars(nLastViewLine);
4619 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4620 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4621 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4623 UpdateWindow();
4626 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4628 FilterWhitespaces(first);
4629 FilterWhitespaces(second);
4632 void CBaseView::FilterWhitespaces(CString& line)
4634 line.Remove(' ');
4635 line.Remove('\t');
4636 line.Remove('\r');
4637 line.Remove('\n');
4640 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4642 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4643 int nEventLine = nLineFromTop + m_nTopLine;
4644 nEventLine--; //we need the index
4645 return nEventLine;
4649 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4651 if (RelayTrippleClick(pMsg))
4652 return TRUE;
4653 return CView::PreTranslateMessage(pMsg);
4657 void CBaseView::ResetUndoStep()
4659 m_AllState.Clear();
4662 void CBaseView::SaveUndoStep()
4664 if (!m_AllState.IsEmpty())
4666 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4668 ResetUndoStep();
4671 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4673 m_pState->addedlines.push_back(index);
4674 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4677 void CBaseView::InsertViewData( int index, const viewdata& data )
4679 m_pState->addedlines.push_back(index);
4680 m_pViewData->InsertData(index, data);
4683 void CBaseView::RemoveViewData( int index )
4685 m_pState->removedlines[index] = m_pViewData->GetData(index);
4686 m_pViewData->RemoveData(index);
4689 void CBaseView::SetViewData( int index, const viewdata& data )
4691 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4692 m_pViewData->SetData(index, data);
4695 void CBaseView::SetViewState( int index, DiffStates state )
4697 m_pState->linestates[index] = m_pViewData->GetState(index);
4698 m_pViewData->SetState(index, state);
4701 void CBaseView::SetViewLine( int index, const CString& sLine )
4703 m_pState->difflines[index] = m_pViewData->GetLine(index);
4704 m_pViewData->SetLine(index, sLine);
4707 void CBaseView::SetViewLineNumber( int index, int linenumber )
4709 int oldLineNumber = m_pViewData->GetLineNumber(index);
4710 if (oldLineNumber != linenumber) {
4711 m_pState->linelines[index] = oldLineNumber;
4712 m_pViewData->SetLineNumber(index, linenumber);
4716 void CBaseView::SetViewLineEnding( int index, EOL ending )
4718 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4719 m_pViewData->SetLineEnding(index, ending);
4723 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4725 if (HasSelection())
4727 start = m_nSelViewBlockStart;
4728 end = m_nSelViewBlockEnd;
4729 return true;
4731 return false;
4734 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4736 RebuildIfNecessary();
4737 if (size() <= screenLine)
4738 return 0;
4739 return m_Screen2View[screenLine].nViewLine;
4742 int CBaseView::Screen2View::size()
4744 RebuildIfNecessary();
4745 return (int)m_Screen2View.size();
4748 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4750 RebuildIfNecessary();
4751 if (size() <= screenLine)
4752 return 0;
4753 return m_Screen2View[screenLine].nViewSubLine;
4756 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4758 RebuildIfNecessary();
4759 return m_Screen2View[screenLine];
4763 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4765 void CBaseView::Screen2View::RebuildIfNecessary()
4767 if (!m_pViewData)
4768 return; // rebuild not necessary
4770 FixScreenedCacheSize(m_pwndLeft);
4771 FixScreenedCacheSize(m_pwndRight);
4772 FixScreenedCacheSize(m_pwndBottom);
4773 if (!m_bFull)
4775 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4777 ResetScreenedViewLineCache(m_pwndLeft, *it);
4778 ResetScreenedViewLineCache(m_pwndRight, *it);
4779 ResetScreenedViewLineCache(m_pwndBottom, *it);
4782 else
4784 ResetScreenedViewLineCache(m_pwndLeft);
4785 ResetScreenedViewLineCache(m_pwndRight);
4786 ResetScreenedViewLineCache(m_pwndBottom);
4788 m_RebuildRanges.clear();
4789 m_bFull = false;
4791 size_t OldSize = m_Screen2View.size();
4792 m_Screen2View.clear();
4793 m_Screen2View.reserve(OldSize); // guess same size
4794 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4796 if (m_pMainFrame->m_bCollapsed)
4798 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4799 ++i;
4800 if (!(i < m_pViewData->GetCount()))
4801 break;
4803 TScreenLineInfo oLineInfo;
4804 oLineInfo.nViewLine = i;
4805 oLineInfo.nViewSubLine = -1; // no wrap
4806 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4808 int nMaxLines = 0;
4809 if (IsLeftViewGood())
4810 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4811 if (IsRightViewGood())
4812 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4813 if (IsBottomViewGood())
4814 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4815 for (int l = 0; l < (nMaxLines-1); ++l)
4817 oLineInfo.nViewSubLine++;
4818 m_Screen2View.push_back(oLineInfo);
4820 oLineInfo.nViewSubLine++;
4822 m_Screen2View.push_back(oLineInfo);
4824 m_pViewData = NULL;
4826 if (IsLeftViewGood())
4827 m_pwndLeft->BuildMarkedWordArray();
4828 if (IsRightViewGood())
4829 m_pwndRight->BuildMarkedWordArray();
4830 if (IsBottomViewGood())
4831 m_pwndBottom->BuildMarkedWordArray();
4832 UpdateLocator();
4833 RecalcAllVertScrollBars();
4834 RecalcAllHorzScrollBars();
4837 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
4839 RebuildIfNecessary();
4841 int nScreenLineCount = (int)m_Screen2View.size();
4843 int nPos = 0;
4844 if (nScreenLineCount>16)
4846 // for enough long data search for last screen
4847 // with viewline less than one we are looking for
4848 // use approximate method (based on) binary search using asymmetric start point
4849 // in form 2**n (determined as MSB of length) to go around division and rounding;
4850 // this effectively looks for bit values from MSB to LSB
4852 int nTestBit;
4853 //GetMostSignificantBitValue
4854 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
4855 nTestBit = nScreenLineCount;
4856 nTestBit |= nTestBit>>1;
4857 nTestBit |= nTestBit>>2;
4858 nTestBit |= nTestBit>>4;
4859 nTestBit |= nTestBit>>8;
4860 nTestBit |= nTestBit>>16;
4861 nTestBit ^= (nTestBit>>1);
4863 while (nTestBit)
4865 int nTestPos = nPos | nTestBit;
4866 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
4868 nPos = nTestPos;
4870 nTestBit >>= 1;
4873 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
4875 nPos++;
4878 return nPos;
4881 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
4882 m_bFull = true;
4884 m_pViewData = pViewData;
4887 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
4889 if (m_bFull)
4890 return;
4892 m_pViewData = pViewData;
4894 TRebuildRange Range;
4895 Range.FirstViewLine=nFirstViewLine;
4896 Range.LastViewLine=nLastViewLine;
4897 m_RebuildRanges.push_back(Range);
4900 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
4902 if (!IsViewGood(pwndView))
4904 return false;
4906 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
4907 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
4908 if (nOldSize == nViewCount)
4910 return false;
4912 pwndView->m_ScreenedViewLine.resize(nViewCount);
4913 return true;
4916 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView)
4918 if (!IsViewGood(pwndView))
4920 return false;
4922 TRebuildRange Range={0, pwndView->GetViewCount()-1};
4923 ResetScreenedViewLineCache(pwndView, Range);
4924 return true;
4927 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range)
4929 if (!IsViewGood(pwndView))
4931 return false;
4933 if (Range.LastViewLine == -1)
4935 return false;
4937 ASSERT(Range.FirstViewLine >= 0);
4938 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
4939 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
4941 pwndView->m_ScreenedViewLine[i].Clear();
4943 return false;
4946 void CBaseView::WrapChanged()
4948 m_nMaxLineLength = -1;
4949 m_nOffsetChar = 0;
4952 void CBaseView::OnEditFind()
4954 if (m_pFindDialog)
4955 return;
4957 m_pFindDialog = new CFindDlg(this);
4958 m_pFindDialog->Create(this);
4960 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
4963 LRESULT CBaseView::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
4965 ASSERT(m_pFindDialog != NULL);
4967 if (m_pFindDialog->IsTerminating())
4969 // invalidate the handle identifying the dialog box.
4970 m_pFindDialog = NULL;
4971 return 0;
4974 if(m_pFindDialog->FindNext())
4976 //read data from dialog
4977 m_sFindText = m_pFindDialog->GetFindString();
4978 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
4979 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
4980 m_bWholeWord = m_pFindDialog->WholeWord();
4982 if (!m_bMatchCase)
4983 m_sFindText = m_sFindText.MakeLower();
4985 BuildFindStringArray();
4986 OnEditFindnext();
4989 return 0;
4992 void CBaseView::OnEditFindnextStart()
4994 if (m_pViewData == nullptr)
4995 return;
4996 if (HasTextSelection())
4998 m_sFindText = GetSelectedText();
4999 m_bMatchCase = false;
5000 m_bLimitToDiff = false;
5001 m_bWholeWord = false;
5002 m_sFindText = m_sFindText.MakeLower();
5004 BuildFindStringArray();
5005 OnEditFindnext();
5007 else
5009 m_sFindText.Empty();
5010 BuildFindStringArray();
5014 void CBaseView::OnEditFindprevStart()
5016 if (m_pViewData == nullptr)
5017 return;
5018 if (HasTextSelection())
5020 m_sFindText = GetSelectedText();
5021 m_bMatchCase = false;
5022 m_bLimitToDiff = false;
5023 m_bWholeWord = false;
5024 m_sFindText = m_sFindText.MakeLower();
5026 BuildFindStringArray();
5027 OnEditFindprev();
5029 else
5031 m_sFindText.Empty();
5032 BuildFindStringArray();
5036 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5038 start = str.Find(m_sFindText);
5039 if ((srchDir==SearchPrevious)&&(start>=0))
5041 int laststart = start;
5044 start = laststart;
5045 laststart = str.Find(m_sFindText, laststart+1);
5046 } while (laststart >= 0);
5048 end = start + m_sFindText.GetLength();
5049 bool bStringFound = (start >= 0);
5050 if (bStringFound && m_bWholeWord)
5052 if (start)
5053 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5055 if (bStringFound)
5057 if (str.GetLength() > end)
5058 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5061 return bStringFound;
5064 void CBaseView::OnEditFindprev()
5066 Search(SearchPrevious);
5069 void CBaseView::OnEditFindnext()
5071 Search(SearchNext);
5074 void CBaseView::Search(SearchDirection srchDir)
5076 if (m_sFindText.IsEmpty())
5077 return;
5078 if(!m_pViewData)
5079 return;
5081 POINT start = m_ptSelectionViewPosEnd;
5082 POINT end;
5083 end.y = m_pViewData->GetCount()-1;
5084 if (end.y < 0)
5085 return;
5087 if (srchDir==SearchNext)
5088 end.x = GetViewLineLength(end.y);
5089 else
5091 end.x = m_ptSelectionViewPosStart.x;
5092 start.x = 0;
5095 if (!HasTextSelection())
5097 start.y = m_ptCaretViewPos.y;
5098 if (srchDir==SearchNext)
5099 start.x = m_ptCaretViewPos.x;
5100 else
5102 start.x = 0;
5103 end.x = m_ptCaretViewPos.x;
5106 CString sSelectedText;
5107 int startline = -1;
5108 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5110 if (nViewLine < 0)
5112 nViewLine = m_pViewData->GetCount()-1;
5113 startline = start.y;
5115 if (nViewLine > end.y)
5117 nViewLine = 0;
5118 startline = start.y;
5120 if (startline >= 0)
5122 if (nViewLine == startline)
5123 break;
5125 switch (m_pViewData->GetState(nViewLine))
5127 case DIFFSTATE_EMPTY:
5128 break;
5129 case DIFFSTATE_UNKNOWN:
5130 case DIFFSTATE_NORMAL:
5131 if (m_bLimitToDiff)
5132 break;
5133 case DIFFSTATE_REMOVED:
5134 case DIFFSTATE_REMOVEDWHITESPACE:
5135 case DIFFSTATE_ADDED:
5136 case DIFFSTATE_ADDEDWHITESPACE:
5137 case DIFFSTATE_WHITESPACE:
5138 case DIFFSTATE_WHITESPACE_DIFF:
5139 case DIFFSTATE_CONFLICTED:
5140 case DIFFSTATE_CONFLICTED_IGNORED:
5141 case DIFFSTATE_CONFLICTADDED:
5142 case DIFFSTATE_CONFLICTEMPTY:
5143 case DIFFSTATE_CONFLICTRESOLVED:
5144 case DIFFSTATE_IDENTICALREMOVED:
5145 case DIFFSTATE_IDENTICALADDED:
5146 case DIFFSTATE_THEIRSREMOVED:
5147 case DIFFSTATE_THEIRSADDED:
5148 case DIFFSTATE_MOVED_FROM:
5149 case DIFFSTATE_MOVED_TO:
5150 case DIFFSTATE_YOURSREMOVED:
5151 case DIFFSTATE_YOURSADDED:
5152 case DIFFSTATE_EDITED:
5154 sSelectedText = GetViewLineChars(nViewLine);
5155 if (nViewLine==start.y)
5156 sSelectedText = srchDir==SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(start.x);
5157 if (!m_bMatchCase)
5158 sSelectedText = sSelectedText.MakeLower();
5159 int startfound = -1;
5160 int endfound = -1;
5161 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5163 HighlightViewLines(nViewLine, nViewLine);
5164 m_ptSelectionViewPosStart.x = startfound;
5165 m_ptSelectionViewPosEnd.x = endfound;
5166 if (nViewLine==start.y)
5168 m_ptSelectionViewPosStart.x += start.x;
5169 m_ptSelectionViewPosEnd.x += start.x;
5171 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5172 m_ptSelectionViewPosStart.y = nViewLine;
5173 m_ptSelectionViewPosEnd.y = nViewLine;
5174 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5175 UpdateViewsCaretPosition();
5176 EnsureCaretVisible();
5177 Invalidate();
5178 return;
5181 break;
5184 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5187 CString CBaseView::GetSelectedText() const
5189 CString sSelectedText;
5190 POINT start = m_ptSelectionViewPosStart;
5191 POINT end = m_ptSelectionViewPosEnd;
5192 if (!HasTextSelection())
5194 if (!HasSelection())
5195 return sSelectedText;
5196 start.y = m_nSelViewBlockStart;
5197 start.x = 0;
5198 end.y = m_nSelViewBlockEnd;
5199 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5201 // first store the selected lines in one CString
5202 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5204 switch (m_pViewData->GetState(nViewLine))
5206 case DIFFSTATE_EMPTY:
5207 break;
5208 case DIFFSTATE_UNKNOWN:
5209 case DIFFSTATE_NORMAL:
5210 case DIFFSTATE_REMOVED:
5211 case DIFFSTATE_REMOVEDWHITESPACE:
5212 case DIFFSTATE_ADDED:
5213 case DIFFSTATE_ADDEDWHITESPACE:
5214 case DIFFSTATE_WHITESPACE:
5215 case DIFFSTATE_WHITESPACE_DIFF:
5216 case DIFFSTATE_CONFLICTED:
5217 case DIFFSTATE_CONFLICTED_IGNORED:
5218 case DIFFSTATE_CONFLICTADDED:
5219 case DIFFSTATE_CONFLICTEMPTY:
5220 case DIFFSTATE_CONFLICTRESOLVED:
5221 case DIFFSTATE_IDENTICALREMOVED:
5222 case DIFFSTATE_IDENTICALADDED:
5223 case DIFFSTATE_THEIRSREMOVED:
5224 case DIFFSTATE_THEIRSADDED:
5225 case DIFFSTATE_MOVED_FROM:
5226 case DIFFSTATE_MOVED_TO:
5227 case DIFFSTATE_YOURSREMOVED:
5228 case DIFFSTATE_YOURSADDED:
5229 case DIFFSTATE_EDITED:
5230 sSelectedText += GetViewLineChars(nViewLine);
5231 sSelectedText += _T("\r\n");
5232 break;
5235 // remove the non-selected chars from the first line, last line and last \r\n
5236 int nLeftCut = start.x;
5237 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5238 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5239 return sSelectedText;
5242 void CBaseView::OnEditGotoline()
5244 if (m_pViewData == NULL)
5245 return;
5246 // find the last and first line number
5247 int nViewLineCount = m_pViewData->GetCount();
5249 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5250 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5252 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5253 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5255 break;
5258 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5260 return;
5262 nLastLineNumber++;
5263 int nFirstLineNumber=1; // first is always 1
5265 CString sText;
5266 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5268 CGotoLineDlg dlg(this);
5269 dlg.SetLabel(sText);
5270 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5271 if (dlg.DoModal() == IDOK)
5273 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5275 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5277 HighlightViewLines(nViewLine, nViewLine);
5278 return;
5284 void CBaseView::OnToggleReadonly()
5286 if (IsReadonlyChangable()) {
5287 SetWritable(IsReadonly());
5291 int CBaseView::SaveFile(int nFlags)
5293 Invalidate();
5294 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5296 CFileTextLines file;
5297 //file.SetSaveParams(m_SaveParams);
5299 for (int i=0; i<m_pViewData->GetCount(); i++)
5301 //only copy non-removed lines
5302 DiffStates state = m_pViewData->GetState(i);
5303 switch (state)
5305 case DIFFSTATE_CONFLICTED:
5306 case DIFFSTATE_CONFLICTED_IGNORED:
5308 int first = i;
5309 int last = i;
5312 last++;
5313 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5314 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5315 for (int j=first; j<last; j++)
5317 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5319 file.Add(_T("======="), EOL_NOENDING);
5320 for (int j=first; j<last; j++)
5322 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5324 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5325 i = last-1;
5327 break;
5328 case DIFFSTATE_EMPTY:
5329 break;
5330 case DIFFSTATE_CONFLICTEMPTY:
5331 case DIFFSTATE_IDENTICALREMOVED:
5332 case DIFFSTATE_REMOVED:
5333 case DIFFSTATE_THEIRSREMOVED:
5334 case DIFFSTATE_YOURSREMOVED:
5335 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5336 if ((nFlags&SAVE_REMOVED) == 0)
5338 // do not save removed lines
5339 break;
5341 default:
5342 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5343 break;
5346 if (!file.Save(m_pWorkingFile->GetFilename()))
5348 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5349 return -1;
5351 m_pWorkingFile->StoreFileAttributes();
5352 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5353 SetModified(FALSE);
5354 CUndo::GetInstance().MarkAsOriginalState();
5355 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5356 return 0;
5357 return file.GetCount();
5359 return 1;
5363 int CBaseView::SaveFileTo(CString sFileName)
5365 if (m_pWorkingFile)
5367 m_pWorkingFile->SetFileName(sFileName);
5368 return SaveFile();
5370 return -1;