Add flag indication user can change Read only mode for view
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob7618315a3d03fc71d82cc010812a2a3255d7a0e4
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 // until ribbon button is ready show state in View title bar
1306 if ((IsLeftViewGood() && m_pwndLeft->m_bReadonlyIsChangable)
1307 || (IsRightViewGood() && m_pwndRight->m_bReadonlyIsChangable)
1308 || (IsBottomViewGood() && m_pwndBottom->m_bReadonlyIsChangable))
1310 // any view has Readonly state changable draw current status
1311 CString sState(IsReadonly() ? "ro" : "rw");
1312 if (!m_bReadonlyIsChangable)
1314 // state is locked
1315 sState = CString("[") + sState + CString("]");
1317 CFont * p_oldFont = pdc->GetCurrentFont();
1318 LOGFONT lf;
1319 p_oldFont->GetLogFont(&lf);
1320 CFont oFont;
1321 if (lf.lfHeight<-14)
1323 lf.lfHeight /= 2;
1325 else
1326 if (lf.lfHeight<-7)
1328 lf.lfHeight = -7;
1331 oFont.CreateFontIndirect(&lf);
1332 pdc->SelectObject(&oFont);
1333 pdc->ExtTextOut(3, 1, ETO_CLIPPED, textrect, sState, NULL);
1334 pdc->SelectObject(p_oldFont);
1337 CString sViewTitle;
1338 if (IsModified())
1340 sViewTitle = _T("* ") + m_sWindowName;
1342 else
1344 sViewTitle = m_sWindowName;
1346 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1347 if (nStringLength > rect.Width())
1349 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1350 sViewTitle = m_sWindowName.Mid(offset);
1352 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1353 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1354 if (this->GetFocus() == this)
1355 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1356 else
1357 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1360 void CBaseView::OnDraw(CDC * pDC)
1362 CRect rcClient;
1363 GetClientRect(rcClient);
1365 int nLineCount = GetLineCount();
1366 int nLineHeight = GetLineHeight();
1368 CDC cacheDC;
1369 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1370 if (m_pCacheBitmap == NULL)
1372 m_pCacheBitmap = new CBitmap;
1373 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1375 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1377 DrawHeader(pDC, rcClient);
1379 CRect rcLine;
1380 rcLine = rcClient;
1381 rcLine.top += nLineHeight+HEADERHEIGHT;
1382 rcLine.bottom = rcLine.top + nLineHeight;
1383 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1384 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1386 int nCurrentLine = m_nTopLine;
1387 bool bBeyondFileLineCached = false;
1388 while (rcLine.top < rcClient.bottom)
1390 if (nCurrentLine < nLineCount)
1392 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1393 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1394 bBeyondFileLineCached = false;
1396 else if (!bBeyondFileLineCached)
1398 DrawMargin(&cacheDC, rcCacheMargin, -1);
1399 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1400 bBeyondFileLineCached = true;
1403 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1405 nCurrentLine ++;
1406 rcLine.OffsetRect(0, nLineHeight);
1409 cacheDC.SelectObject(pOldBitmap);
1410 cacheDC.DeleteDC();
1413 bool CBaseView::IsStateConflicted(DiffStates state)
1415 switch (state)
1417 case DIFFSTATE_CONFLICTED:
1418 case DIFFSTATE_CONFLICTED_IGNORED:
1419 case DIFFSTATE_CONFLICTEMPTY:
1420 case DIFFSTATE_CONFLICTADDED:
1421 return true;
1423 return false;
1426 bool CBaseView::IsStateEmpty(DiffStates state)
1428 switch (state)
1430 case DIFFSTATE_CONFLICTEMPTY:
1431 case DIFFSTATE_UNKNOWN:
1432 case DIFFSTATE_EMPTY:
1433 return true;
1435 return false;
1438 bool CBaseView::IsStateRemoved(DiffStates state)
1440 switch (state)
1442 case DIFFSTATE_REMOVED:
1443 case DIFFSTATE_MOVED_FROM:
1444 case DIFFSTATE_THEIRSREMOVED:
1445 case DIFFSTATE_YOURSREMOVED:
1446 case DIFFSTATE_IDENTICALREMOVED:
1447 return true;
1449 return false;
1452 DiffStates CBaseView::ResolveState(DiffStates state)
1454 if (IsStateConflicted(state))
1456 if (state == DIFFSTATE_CONFLICTEMPTY)
1457 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1458 else
1459 return DIFFSTATE_CONFLICTRESOLVED;
1461 return state;
1465 bool CBaseView::IsLineEmpty(int nLineIndex)
1467 if (m_pViewData == 0)
1468 return FALSE;
1469 int nViewLine = GetViewLineForScreen(nLineIndex);
1470 return IsViewLineEmpty(nViewLine);
1473 bool CBaseView::IsViewLineEmpty(int nViewLine)
1475 if (m_pViewData == 0)
1476 return FALSE;
1477 const DiffStates state = m_pViewData->GetState(nViewLine);
1478 return IsStateEmpty(state);
1481 bool CBaseView::IsLineRemoved(int nLineIndex)
1483 if (m_pViewData == 0)
1484 return FALSE;
1485 int nViewLine = GetViewLineForScreen(nLineIndex);
1486 return IsViewLineRemoved(nViewLine);
1489 bool CBaseView::IsViewLineRemoved(int nViewLine)
1491 if (m_pViewData == 0)
1492 return FALSE;
1493 const DiffStates state = m_pViewData->GetState(nViewLine);
1494 return IsStateRemoved(state);
1497 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1499 if (m_pViewData == 0)
1500 return false;
1501 const DiffStates state = m_pViewData->GetState(nLineIndex);
1502 return IsStateConflicted(state);
1505 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1507 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1510 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1512 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1515 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1517 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1518 return;
1519 int viewLine = GetViewLineForScreen(nLineIndex);
1520 EOL ending = m_pViewData->GetLineEnding(viewLine);
1521 if (m_bIconLFs)
1523 HICON hEndingIcon = NULL;
1524 switch (ending)
1526 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1527 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1528 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1529 default: return;
1531 if (origin.x < (rc.left-GetCharWidth()))
1532 return;
1533 // If EOL style has changed, color end-of-line markers as inline differences.
1535 m_bShowInlineDiff && m_pOtherViewData &&
1536 (viewLine < m_pOtherViewData->GetCount()) &&
1537 (ending != EOL_NOENDING) &&
1538 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1539 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1542 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1545 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1547 else
1549 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1550 CPen * oldpen = pDC->SelectObject(&pen);
1551 int yMiddle = origin.y + rc.Height()/2;
1552 int xMiddle = origin.x+GetCharWidth()/2;
1553 bool bMultiline = false;
1554 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1556 if (GetLineLength(nLineIndex+1))
1558 // multiline
1559 bMultiline = true;
1560 pDC->MoveTo(origin.x, yMiddle-2);
1561 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1562 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1563 pDC->LineTo(origin.x, yMiddle+2);
1565 else if (GetLineLength(nLineIndex) == 0)
1566 bMultiline = true;
1568 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1569 bMultiline = true;
1571 if (!bMultiline)
1573 switch (ending)
1575 case EOL_AUTOLINE:
1576 case EOL_CRLF:
1577 // arrow from top to middle+2, then left
1578 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1579 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1580 case EOL_CR:
1581 // arrow from right to left
1582 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1583 pDC->LineTo(origin.x, yMiddle);
1584 pDC->LineTo(origin.x+4, yMiddle+4);
1585 pDC->MoveTo(origin.x, yMiddle);
1586 pDC->LineTo(origin.x+4, yMiddle-4);
1587 break;
1588 case EOL_LFCR:
1589 // from right-upper to left then down
1590 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1591 pDC->LineTo(xMiddle, yMiddle-2);
1592 pDC->LineTo(xMiddle, rc.bottom-1);
1593 pDC->LineTo(xMiddle+4, rc.bottom-5);
1594 pDC->MoveTo(xMiddle, rc.bottom-1);
1595 pDC->LineTo(xMiddle-4, rc.bottom-5);
1596 break;
1597 case EOL_LF:
1598 // arrow from top to bottom
1599 pDC->MoveTo(xMiddle, rc.top);
1600 pDC->LineTo(xMiddle, rc.bottom-1);
1601 pDC->LineTo(xMiddle+4, rc.bottom-5);
1602 pDC->MoveTo(xMiddle, rc.bottom-1);
1603 pDC->LineTo(xMiddle-4, rc.bottom-5);
1604 break;
1605 case EOL_FF: // Form Feed, U+000C
1606 case EOL_NEL: // Next Line, U+0085
1607 case EOL_LS: // Line Separator, U+2028
1608 case EOL_PS: // Paragraph Separator, U+2029
1609 // draw a horizontal line at the bottom of this line
1610 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1611 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1612 pDC->LineTo(origin.x, rc.bottom-2);
1613 pDC->LineTo(origin.x+5, rc.bottom-2);
1614 pDC->MoveTo(origin.x, rc.bottom-2);
1615 pDC->LineTo(origin.x+1, rc.bottom-6);
1616 break;
1617 default: // other EOLs
1618 // arrow from top right to bottom left
1619 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1620 pDC->LineTo(origin.x, rc.bottom-1);
1621 pDC->LineTo(origin.x+5, rc.bottom-2);
1622 pDC->MoveTo(origin.x, rc.bottom-1);
1623 pDC->LineTo(origin.x+1, rc.bottom-6);
1624 break;
1625 case EOL_NOENDING:
1626 break;
1629 pDC->SelectObject(oldpen);
1633 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1635 if (!m_bShowSelection)
1636 return;
1638 int nSelBlockStart;
1639 int nSelBlockEnd;
1640 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1641 return;
1643 const int THICKNESS = 2;
1644 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1646 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1647 int nSubLine = GetSubLineOffset(nLineIndex);
1648 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1649 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1651 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1654 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1655 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1657 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1661 void CBaseView::DrawTextLine(
1662 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1664 ASSERT(nLineIndex < GetLineCount());
1665 int nViewLine = GetViewLineForScreen(nLineIndex);
1666 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1668 LineColors lineCols = GetLineColors(nViewLine);
1670 CString sViewLine = GetViewLineChars(nViewLine);
1671 // mark selection
1672 if (m_bShowSelection && HasTextSelection())
1674 // has this line selection ?
1675 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1677 int nViewLineLength = sViewLine.GetLength();
1679 // first suppose the whole line is selected
1680 int selectedStart = 0;
1681 int selectedEnd = nViewLineLength;
1683 // the view line is partially selected
1684 if (m_ptSelectionViewPosStart.y == nViewLine)
1686 selectedStart = m_ptSelectionViewPosStart.x;
1689 if (m_ptSelectionViewPosEnd.y == nViewLine)
1691 selectedEnd = m_ptSelectionViewPosEnd.x;
1693 // apply selection coloring
1694 // First enforce start and end point
1695 lineCols.SplitBlock(selectedStart);
1696 lineCols.SplitBlock(selectedEnd);
1697 // change color of affected parts
1698 long intenseColorScale = m_bFocused ? 70 : 30;
1699 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1700 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1702 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1703 if (it->second.shot == it->second.background)
1705 it->second.shot = crBk;
1707 it->second.background = crBk;
1708 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1713 // TODO: remove duplicate from selection and mark
1714 if (!m_sMarkedWord.IsEmpty())
1716 int nMarkLength = m_sMarkedWord.GetLength();
1717 //int nViewLineLength = sViewLine.GetLength();
1718 const TCHAR * text = sViewLine;
1719 const TCHAR * findText = text;
1720 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1722 int nMarkStart = static_cast<int>(findText - text);
1723 int nMarkEnd = nMarkStart + nMarkLength;
1724 // First enforce start and end point
1725 lineCols.SplitBlock(nMarkStart);
1726 lineCols.SplitBlock(nMarkEnd);
1727 // change color of affected parts
1728 const long int nIntenseColorScale = 200;
1729 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1730 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1732 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1733 if (it->second.shot == it->second.background)
1735 it->second.shot = crBk;
1737 it->second.background = crBk;
1738 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1740 findText += nMarkLength;
1743 if (!m_sFindText.IsEmpty())
1745 int nMarkStart = 0;
1746 int nMarkEnd = 0;
1747 int nStringPos = nMarkStart;
1748 CString searchLine = sViewLine;
1749 if (!m_bMatchCase)
1750 searchLine.MakeLower();
1751 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1753 // First enforce start and end point
1754 lineCols.SplitBlock(nMarkStart+nStringPos);
1755 lineCols.SplitBlock(nMarkEnd+nStringPos);
1756 // change color of affected parts
1757 const long int nIntenseColorScale = 30;
1758 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1759 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1761 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1762 if (it->second.shot == it->second.background)
1764 it->second.shot = crBk;
1766 it->second.background = crBk;
1767 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1769 searchLine = searchLine.Mid(nMarkEnd);
1770 nStringPos = nMarkEnd;
1774 // @ this point we may cache data for next line which may be same in wrapped mode
1776 int nTextOffset = 0;
1777 int nSubline = GetSubLineOffset(nLineIndex);
1778 for (int n=0; n<nSubline; n++)
1780 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1781 nTextOffset += sLine.GetLength();
1784 CString sLine = GetLineChars(nLineIndex);
1785 int nLineLength = sLine.GetLength();
1786 CString sLineExp = ExpandChars(sLine);
1787 LPCTSTR textExp = sLineExp;
1788 //int nLineLengthExp = sLineExp.GetLength();
1789 int nStartExp = 0;
1790 int nLeft = coords.x;
1791 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1793 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1794 ++itEnd;
1795 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1796 int nEnd = nLineLength;
1797 if (itEnd != lineCols.end())
1799 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1801 int nBlockLength = nEnd - nStart;
1802 if (nBlockLength > 0 && nEnd>=0)
1804 pDC->SetBkColor(itStart->second.background);
1805 pDC->SetTextColor(itStart->second.text);
1806 int nEndExp = CountExpandedChars(sLine, nEnd);
1807 int nTextLength = nEndExp - nStartExp;
1808 LPCTSTR p_zBlockText = textExp + nStartExp;
1809 SIZE Size;
1810 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1811 int nRight = nLeft + Size.cx;
1812 if ((nRight > rc.left) && (nLeft < rc.right))
1814 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1815 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1816 // is 4094 (4095 doesn't work anymore).
1817 // So we limit the length here to that 4094 chars.
1818 // In case we're scrolled to the right, there's no need to draw the string
1819 // from way outside our window, so we also offset the drawing to the start of the window.
1820 // This reduces the string length as well.
1821 int offset = 0;
1822 int leftcoord = nLeft;
1823 if (nLeft < 0)
1825 offset = (-nLeft/GetCharWidth());
1826 nTextLength -= offset;
1827 leftcoord = nLeft % GetCharWidth();
1830 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1831 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1833 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1836 nLeft = nRight;
1837 coords.x = nRight;
1838 nStartExp = nEndExp;
1843 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1845 if (nLineIndex >= GetLineCount())
1846 nLineIndex = -1;
1847 ASSERT(nLineIndex >= -1);
1849 if ((nLineIndex == -1) || !m_pViewData)
1851 // Draw line beyond the text
1852 COLORREF crBkgnd, crText;
1853 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1854 pDC->FillSolidRect(rc, crBkgnd);
1855 return;
1858 int viewLine = GetViewLineForScreen(nLineIndex);
1859 if (m_pMainFrame->m_bCollapsed)
1861 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1863 COLORREF crBkgnd, crText;
1864 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1865 pDC->FillSolidRect(rc, crBkgnd);
1867 const int THICKNESS = 2;
1868 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1869 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1870 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1871 pDC->SetBkColor(crBkgnd);
1872 CRect rect = rc;
1873 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1874 return;
1878 DiffStates diffState = m_pViewData->GetState(viewLine);
1879 COLORREF crBkgnd, crText;
1880 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1882 if (diffState == DIFFSTATE_CONFLICTED)
1884 // conflicted lines are shown without 'text' on them
1885 CRect rect = rc;
1886 pDC->FillSolidRect(rc, crBkgnd);
1887 // now draw some faint text patterns
1888 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1889 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1890 DrawBlockLine(pDC, rc, nLineIndex);
1891 return;
1894 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1895 CString sLine = GetLineChars(nLineIndex);
1896 if (sLine.IsEmpty())
1898 pDC->FillSolidRect(rc, crBkgnd);
1899 DrawBlockLine(pDC, rc, nLineIndex);
1900 DrawLineEnding(pDC, rc, nLineIndex, origin);
1901 return;
1904 CheckOtherView();
1906 // Draw the line
1908 pDC->SelectObject(GetFont(FALSE, FALSE));
1910 DrawTextLine(pDC, rc, nLineIndex, origin);
1912 // draw white space after the end of line
1913 CRect frect = rc;
1914 if (origin.x > frect.left)
1915 frect.left = origin.x;
1916 if (frect.right > frect.left)
1917 pDC->FillSolidRect(frect, crBkgnd);
1919 // draw the whitespace chars
1920 LPCTSTR pszChars = (LPCWSTR)sLine;
1921 if (m_bViewWhitespace)
1923 int xpos = 0;
1924 int y = rc.top + (rc.bottom-rc.top)/2;
1926 int nActualOffset = 0;
1927 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1929 if (*pszChars == _T('\t'))
1930 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1931 else
1932 nActualOffset++;
1933 pszChars++;
1935 if (nActualOffset > m_nOffsetChar)
1936 pszChars--;
1938 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1939 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1940 while (*pszChars)
1942 switch (*pszChars)
1944 case _T('\t'):
1946 // draw an arrow
1947 CPen * oldPen = pDC->SelectObject(&pen);
1948 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1949 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1950 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1951 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1952 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1953 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1954 xpos += nSpaces;
1955 pDC->SelectObject(oldPen);
1957 break;
1958 case _T(' '):
1960 // draw a small dot
1961 CPen * oldPen = pDC->SelectObject(&pen2);
1962 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1963 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1964 xpos++;
1965 pDC->SelectObject(oldPen);
1967 break;
1968 default:
1969 xpos++;
1970 break;
1972 pszChars++;
1975 DrawBlockLine(pDC, rc, nLineIndex);
1976 if (origin.x >= rc.left)
1977 DrawLineEnding(pDC, rc, nLineIndex, origin);
1980 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1982 if (nCount <= 0)
1984 line = _T("");
1985 return;
1988 int nTabSize = GetTabSize();
1990 int nActualOffset = CountExpandedChars(sLine, nOffset);
1992 LPCTSTR pszChars = (LPCWSTR)sLine;
1993 pszChars += nOffset;
1994 int nLength = nCount;
1996 int nTabCount = 0;
1997 for (int i=0; i<nLength; i++)
1999 if (pszChars[i] == _T('\t'))
2000 nTabCount ++;
2003 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2004 int nCurPos = 0;
2005 if (nTabCount > 0 || m_bViewWhitespace)
2007 for (int i=0; i<nLength; i++)
2009 if (pszChars[i] == _T('\t'))
2011 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2012 while (nSpaces > 0)
2014 pszBuf[nCurPos ++] = _T(' ');
2015 nSpaces --;
2018 else
2020 pszBuf[nCurPos] = pszChars[i];
2021 nCurPos ++;
2025 else
2027 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2028 nCurPos = nLength;
2030 pszBuf[nCurPos] = 0;
2031 line.ReleaseBuffer();
2034 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2036 CString sRet;
2037 int nLength = sLine.GetLength();
2038 ExpandChars(sLine, nOffset, nLength, sRet);
2039 return sRet;
2042 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2044 int nTabSize = GetTabSize();
2046 int nActualOffset = 0;
2047 for (int i=0; i<nLength; i++)
2049 if (sLine[i] == _T('\t'))
2050 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2051 else
2052 nActualOffset ++;
2054 return nActualOffset;
2057 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2059 if (m_pwndLeft)
2060 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2061 if (m_pwndRight)
2062 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2063 if (m_pwndBottom)
2064 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2065 if (m_pwndLocator)
2066 m_pwndLocator->Invalidate();
2069 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2071 //almost the same as ScrollAllToLine, but try to put the line in the
2072 //middle of the view, not on top
2073 int nNewTopLine = nNewLine - GetScreenLines()/2;
2074 if (nNewTopLine < 0)
2075 nNewTopLine = 0;
2076 if (nNewTopLine >= (int)m_Screen2View.size())
2077 nNewTopLine = (int)m_Screen2View.size()-1;
2078 if (bAll)
2079 ScrollAllToLine(nNewTopLine);
2080 else
2081 ScrollToLine(nNewTopLine);
2084 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2086 return TRUE;
2089 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2091 if (CView::OnCreate(lpCreateStruct) == -1)
2092 return -1;
2094 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2095 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2096 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2097 m_lfBaseFont.lfHeight = 0;
2098 m_lfBaseFont.lfWeight = FW_NORMAL;
2099 m_lfBaseFont.lfItalic = FALSE;
2100 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2101 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2102 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2103 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2104 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2106 return 0;
2109 void CBaseView::OnDestroy()
2111 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2113 m_pFindDialog->SendMessage(WM_CLOSE);
2114 return;
2116 CView::OnDestroy();
2117 DeleteFonts();
2118 ReleaseBitmap();
2121 void CBaseView::OnSize(UINT nType, int cx, int cy)
2123 CView::OnSize(nType, cx, cy);
2124 ReleaseBitmap();
2126 m_nScreenLines = -1;
2127 m_nScreenChars = -1;
2128 if (m_nLastScreenChars != GetScreenChars())
2130 BuildAllScreen2ViewVector();
2131 m_nLastScreenChars = m_nScreenChars;
2132 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2134 // if we're in wrap mode, the line wrapping most likely changed
2135 // and that means we have to redraw the whole window, not just the
2136 // scrolled part.
2137 Invalidate(FALSE);
2139 else
2141 // make sure the view header is redrawn
2142 CRect rcScroll;
2143 GetClientRect(&rcScroll);
2144 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2145 InvalidateRect(&rcScroll, FALSE);
2148 else
2150 // make sure the view header is redrawn
2151 CRect rcScroll;
2152 GetClientRect(&rcScroll);
2153 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2154 InvalidateRect(&rcScroll, FALSE);
2156 UpdateLocator();
2157 RecalcVertScrollBar();
2158 RecalcHorzScrollBar();
2160 UpdateCaret();
2163 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2165 if (m_pwndLeft)
2166 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2167 if (m_pwndRight)
2168 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2169 if (m_pwndBottom)
2170 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2171 if (m_pwndLocator)
2172 m_pwndLocator->Invalidate();
2173 return CView::OnMouseWheel(nFlags, zDelta, pt);
2176 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2178 if (m_pwndLeft)
2179 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2180 if (m_pwndRight)
2181 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2182 if (m_pwndBottom)
2183 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2184 if (m_pwndLocator)
2185 m_pwndLocator->Invalidate();
2188 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2190 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2191 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2193 if (bControl || bShift)
2195 if (m_pMainFrame->m_bWrapLines)
2196 return;
2197 // Ctrl-Wheel scrolls sideways
2198 ScrollSide(-zDelta/30);
2200 else
2202 ScrollVertical(zDelta);
2206 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2208 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2209 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2211 if (bControl || bShift)
2213 ScrollVertical(zDelta);
2215 else
2217 if (m_pMainFrame->m_bWrapLines)
2218 return;
2219 // Ctrl-Wheel scrolls sideways
2220 ScrollSide(-zDelta/30);
2224 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2226 if (nHitTest == HTCLIENT)
2228 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2230 if (m_nMouseLine < (int)m_Screen2View.size())
2232 if (m_nMouseLine >= 0)
2234 int viewLine = GetViewLineForScreen(m_nMouseLine);
2235 if (viewLine < m_pViewData->GetCount())
2237 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2239 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2240 return TRUE;
2246 if (m_mouseInMargin)
2248 ::SetCursor(m_margincursor);
2249 return TRUE;
2251 if (m_nMouseLine >= 0)
2253 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2254 return TRUE;
2257 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2258 return TRUE;
2260 return CView::OnSetCursor(pWnd, nHitTest, message);
2263 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2265 CView::OnKillFocus(pNewWnd);
2266 m_bFocused = FALSE;
2267 UpdateCaret();
2268 Invalidate();
2271 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2273 CView::OnSetFocus(pOldWnd);
2274 m_bFocused = TRUE;
2275 UpdateCaret();
2276 Invalidate();
2279 int CBaseView::GetLineFromPoint(CPoint point)
2281 ScreenToClient(&point);
2282 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2285 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2287 if (!this->IsWindowVisible())
2288 return;
2290 CIconMenu popup;
2291 if (!popup.CreatePopupMenu())
2292 return;
2294 AddContextItems(popup, state);
2296 CompensateForKeyboard(point);
2298 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2299 ResetUndoStep();
2300 switch (cmd)
2302 // 2-pane view commands; target is right view
2303 case POPUPCOMMAND_USELEFTBLOCK:
2304 m_pwndRight->UseLeftBlock();
2305 break;
2306 case POPUPCOMMAND_USELEFTFILE:
2307 m_pwndRight->UseLeftFile();
2308 break;
2309 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2310 m_pwndRight->UseBothLeftFirst();
2311 break;
2312 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2313 m_pwndRight->UseBothRightFirst();
2314 break;
2315 // 3-pane view commands; target is bottom view
2316 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2317 m_pwndBottom->UseBothRightFirst();
2318 break;
2319 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2320 m_pwndBottom->UseBothLeftFirst();
2321 break;
2322 case POPUPCOMMAND_USEYOURBLOCK:
2323 m_pwndBottom->UseRightBlock();
2324 break;
2325 case POPUPCOMMAND_USEYOURFILE:
2326 m_pwndBottom->UseRightFile();
2327 break;
2328 case POPUPCOMMAND_USETHEIRBLOCK:
2329 m_pwndBottom->UseLeftBlock();
2330 break;
2331 case POPUPCOMMAND_USETHEIRFILE:
2332 m_pwndBottom->UseLeftFile();
2333 break;
2334 // copy, cut and paste commands
2335 case ID_EDIT_COPY:
2336 OnEditCopy();
2337 break;
2338 case ID_EDIT_CUT:
2339 OnEditCut();
2340 break;
2341 case ID_EDIT_PASTE:
2342 OnEditPaste();
2343 break;
2344 default:
2345 return;
2346 } // switch (cmd)
2347 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2348 return;
2351 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2353 if (!m_pViewData)
2354 return;
2356 int nViewBlockStart = -1;
2357 int nViewBlockEnd = -1;
2358 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2359 if ((point.x >= 0) && (point.y >= 0))
2361 int nLine = GetLineFromPoint(point)-1;
2362 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2364 int nViewLine = GetViewLineForScreen(nLine);
2365 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2367 ClearSelection(); // Clear text-copy selection
2369 nViewBlockStart = nViewLine;
2370 nViewBlockEnd = nViewLine;
2371 DiffStates state = m_pViewData->GetState(nViewLine);
2372 while (nViewBlockStart > 0)
2374 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2375 if (!LinesInOneChange(-1, state, lineState))
2376 break;
2377 nViewBlockStart--;
2380 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2382 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2383 if (!LinesInOneChange(1, state, lineState))
2384 break;
2385 nViewBlockEnd++;
2388 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2389 UpdateCaretPosition(point);
2394 // FixSelection(); fix selection range
2395 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2396 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2398 DiffStates state = DIFFSTATE_UNKNOWN;
2399 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2401 // find a more 'relevant' state in the selection
2402 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2404 state = m_pViewData->GetState(i);
2405 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2406 break;
2409 OnContextMenu(point, state);
2412 void CBaseView::RefreshViews()
2414 if (m_pwndLeft)
2416 m_pwndLeft->UpdateStatusBar();
2417 m_pwndLeft->Invalidate();
2419 if (m_pwndRight)
2421 m_pwndRight->UpdateStatusBar();
2422 m_pwndRight->Invalidate();
2424 if (m_pwndBottom)
2426 m_pwndBottom->UpdateStatusBar();
2427 m_pwndBottom->Invalidate();
2429 if (m_pwndLocator)
2430 m_pwndLocator->Invalidate();
2433 void CBaseView::GoToFirstDifference()
2435 SetCaretToFirstViewLine();
2436 SelectNextBlock(1, false, false);
2439 void CBaseView::GoToFirstConflict()
2441 SetCaretToFirstViewLine();
2442 SelectNextBlock(1, true, false);
2445 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2447 ClearSelection();
2448 SetupAllSelection(nStart, max(nStart, nEnd));
2450 UpdateCaretPosition(SetupPoint(0, nStart));
2451 Invalidate();
2454 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2456 ClearSelection();
2457 SetupAllViewSelection(nStart, max(nStart, nEnd));
2459 UpdateCaretViewPosition(SetupPoint(0, nStart));
2460 Invalidate();
2463 void CBaseView::SetupAllViewSelection(int start, int end)
2465 SetupViewSelection(m_pwndBottom, start, end);
2466 SetupViewSelection(m_pwndLeft, start, end);
2467 SetupViewSelection(m_pwndRight, start, end);
2470 void CBaseView::SetupAllSelection(int start, int end)
2472 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2475 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2477 void CBaseView::SetupSelection(int start, int end)
2479 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2482 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2484 if (!IsViewGood(view))
2485 return;
2486 view->SetupViewSelection(start, end);
2489 void CBaseView::SetupViewSelection(int start, int end)
2491 // clear text selection before setting line selection ?
2492 m_nSelViewBlockStart = start;
2493 m_nSelViewBlockEnd = end;
2494 Invalidate();
2498 void CBaseView::OnMergePreviousconflict()
2500 SelectNextBlock(-1, true);
2503 void CBaseView::OnMergeNextconflict()
2505 SelectNextBlock(1, true);
2508 void CBaseView::OnMergeNextdifference()
2510 SelectNextBlock(1, false);
2513 void CBaseView::OnMergePreviousdifference()
2515 SelectNextBlock(-1, false);
2518 bool CBaseView::HasNextConflict()
2520 return SelectNextBlock(1, true, true, true);
2523 bool CBaseView::HasPrevConflict()
2525 return SelectNextBlock(-1, true, true, true);
2528 bool CBaseView::HasNextDiff()
2530 return SelectNextBlock(1, false, true, true);
2533 bool CBaseView::HasPrevDiff()
2535 return SelectNextBlock(-1, false, true, true);
2538 bool CBaseView::LinesInOneChange(int direction,
2539 DiffStates initialLineState, DiffStates currentLineState)
2541 // Checks whether all the adjacent lines starting from the initial line
2542 // and up to the current line form the single change
2544 // Do not distinguish between moved and added/removed lines
2545 if (initialLineState == DIFFSTATE_MOVED_TO)
2546 initialLineState = DIFFSTATE_ADDED;
2547 if (initialLineState == DIFFSTATE_MOVED_FROM)
2548 initialLineState = DIFFSTATE_REMOVED;
2549 if (currentLineState == DIFFSTATE_MOVED_TO)
2550 currentLineState = DIFFSTATE_ADDED;
2551 if (currentLineState == DIFFSTATE_MOVED_FROM)
2552 currentLineState = DIFFSTATE_REMOVED;
2554 // First of all, if the two lines have identical states, they surely
2555 // belong to one change.
2556 if (initialLineState == currentLineState)
2557 return true;
2559 // Either we move down and initial line state is "added" or "removed" and
2560 // current line state is "empty"...
2561 if (direction > 0)
2563 if (currentLineState == DIFFSTATE_EMPTY)
2565 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2566 return true;
2568 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2569 return true;
2571 // ...or we move up and initial line state is "empty" and current line
2572 // state is "added" or "removed".
2573 if (direction < 0)
2575 if (initialLineState == DIFFSTATE_EMPTY)
2577 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2578 return true;
2580 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2581 return true;
2583 return false;
2586 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2588 if (! m_pViewData)
2589 return false;
2591 const int linesCount = (int)m_Screen2View.size();
2592 if(linesCount == 0)
2593 return false;
2595 int nCenterPos = GetCaretPosition().y;
2596 int nLimit = -1;
2597 if (nDirection > 0)
2598 nLimit = linesCount;
2600 if (nCenterPos >= linesCount)
2601 nCenterPos = linesCount-1;
2603 if (bSkipEndOfCurrentBlock)
2605 // Find end of current block
2606 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2607 while (nCenterPos != nLimit)
2609 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2610 if (!LinesInOneChange(nDirection, state, lineState))
2611 break;
2612 nCenterPos += nDirection;
2616 // Find next diff/conflict block
2617 while (nCenterPos != nLimit)
2619 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2620 if (!bConflict &&
2621 (linestate != DIFFSTATE_NORMAL) &&
2622 (linestate != DIFFSTATE_UNKNOWN))
2624 break;
2626 if (bConflict &&
2627 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2628 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2629 (linestate == DIFFSTATE_CONFLICTED) ||
2630 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2632 break;
2635 nCenterPos += nDirection;
2637 if (nCenterPos == nLimit)
2638 return false;
2639 if (dryrun)
2640 return (nCenterPos != nLimit);
2642 // Find end of new block
2643 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2644 int nBlockEnd = nCenterPos;
2645 const int maxAllowedLine = nLimit-nDirection;
2646 while (nBlockEnd != maxAllowedLine)
2648 const int lineIndex = nBlockEnd + nDirection;
2649 if (lineIndex >= linesCount)
2650 break;
2651 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2652 if (!LinesInOneChange(nDirection, state, lineState))
2653 break;
2654 nBlockEnd += nDirection;
2657 int nTopPos = nCenterPos - (GetScreenLines()/2);
2658 if (nTopPos < 0)
2659 nTopPos = 0;
2661 POINT ptCaretPos = {0, nCenterPos};
2662 SetCaretPosition(ptCaretPos);
2663 ClearSelection();
2664 if (nDirection > 0)
2665 SetupAllSelection(nCenterPos, nBlockEnd);
2666 else
2667 SetupAllSelection(nBlockEnd, nCenterPos);
2669 ScrollAllToLine(nTopPos, FALSE);
2670 RecalcAllVertScrollBars(TRUE);
2671 SetCaretToLineStart();
2672 EnsureCaretVisible();
2673 OnNavigateNextinlinediff();
2675 UpdateViewsCaretPosition();
2676 UpdateCaret();
2677 ShowDiffLines(nCenterPos);
2678 return true;
2681 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2683 if (pNMHDR->idFrom != (UINT)m_hWnd)
2684 return FALSE;
2686 CString strTipText;
2687 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2689 DWORD pos = GetMessagePos();
2690 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2691 ScreenToClient(&point);
2692 const int nLine = GetButtonEventLineIndex(point);
2694 if (nLine >= 0)
2696 int nViewLine = GetViewLineForScreen(nLine);
2697 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2699 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)
2701 strTipText.Format(IDS_MOVED_TO_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2703 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO)
2705 strTipText.Format(IDS_MOVED_FROM_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2711 *pResult = 0;
2712 if (strTipText.IsEmpty())
2713 return TRUE;
2715 // need to handle both ANSI and UNICODE versions of the message
2716 if (pNMHDR->code == TTN_NEEDTEXTA)
2718 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2719 pTTTA->lpszText = m_szTip;
2720 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2722 else
2724 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2725 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2726 pTTTW->lpszText = m_wszTip;
2729 return TRUE; // message was handled
2732 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2734 CRect rcClient;
2735 GetClientRect(rcClient);
2736 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2737 int marginwidth = MARGINWIDTH;
2738 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2740 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2742 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2744 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2746 // inside the header part of the view (showing the filename)
2747 pTI->hwnd = this->m_hWnd;
2748 this->GetClientRect(&pTI->rect);
2749 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2750 pTI->uId = (UINT)m_hWnd;
2751 pTI->lpszText = LPSTR_TEXTCALLBACK;
2753 // we want multi line tooltips
2754 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2755 if (pToolTip->GetSafeHwnd() != NULL)
2757 pToolTip->SetMaxTipWidth(INT_MAX);
2760 return (textrect.PtInRect(point) ? 1 : 2);
2763 return -1;
2766 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2768 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2769 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2771 switch (nChar)
2773 case VK_TAB:
2774 if ((nChar == '\t') && bControl)
2776 if (this==m_pwndLeft)
2778 if (IsViewGood(m_pwndRight))
2780 m_pwndRight->SetFocus();
2782 else if (IsViewGood(m_pwndBottom))
2784 m_pwndBottom->SetFocus();
2787 else if (this==m_pwndRight)
2789 if (IsViewGood(m_pwndBottom))
2791 m_pwndBottom->SetFocus();
2793 else if (IsViewGood(m_pwndLeft))
2795 m_pwndLeft->SetFocus();
2798 else if (this==m_pwndBottom)
2800 if (IsViewGood(m_pwndLeft))
2802 m_pwndLeft->SetFocus();
2804 else if (IsViewGood(m_pwndRight))
2806 m_pwndRight->SetFocus();
2810 break;
2811 case VK_PRIOR:
2813 POINT ptCaretPos = GetCaretPosition();
2814 ptCaretPos.y -= GetScreenLines();
2815 ptCaretPos.y = max(ptCaretPos.y, 0);
2816 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2817 SetCaretPosition(ptCaretPos);
2818 OnCaretMove(MOVELEFT, bShift);
2819 ShowDiffLines(ptCaretPos.y);
2821 break;
2822 case VK_NEXT:
2824 POINT ptCaretPos = GetCaretPosition();
2825 ptCaretPos.y += GetScreenLines();
2826 if (ptCaretPos.y >= GetLineCount())
2827 ptCaretPos.y = GetLineCount()-1;
2828 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2829 SetCaretPosition(ptCaretPos);
2830 OnCaretMove(MOVERIGHT, bShift);
2831 ShowDiffLines(ptCaretPos.y);
2833 break;
2834 case VK_HOME:
2836 if (bControl)
2838 ScrollAllToLine(0);
2839 SetCaretToViewStart();
2840 m_nCaretGoalPos = 0;
2841 if (bShift)
2842 AdjustSelection(MOVELEFT);
2843 else
2844 ClearSelection();
2845 UpdateCaret();
2847 else
2849 SetCaretToLineStart();
2850 m_nCaretGoalPos = 0;
2851 OnCaretMove(MOVERIGHT, bShift);
2852 ScrollAllToChar(0);
2855 break;
2856 case VK_END:
2858 if (bControl)
2860 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2861 POINT ptCaretPos;
2862 ptCaretPos.y = GetLineCount()-1;
2863 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2864 SetCaretAndGoalPosition(ptCaretPos);
2865 if (bShift)
2866 AdjustSelection(MOVERIGHT);
2867 else
2868 ClearSelection();
2870 else
2872 POINT ptCaretPos = GetCaretPosition();
2873 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2874 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2876 ptCaretPos.x--;
2878 SetCaretAndGoalPosition(ptCaretPos);
2879 OnCaretMove(bShift);
2882 break;
2883 case VK_BACK:
2884 if (IsWritable())
2886 if (! HasTextSelection())
2888 POINT ptCaretPos = GetCaretPosition();
2889 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2890 break;
2891 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2892 if (bControl)
2893 MoveCaretWordLeft();
2894 else
2896 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
2900 m_ptSelectionViewPosStart = GetCaretViewPosition();
2902 RemoveSelectedText();
2904 break;
2905 case VK_DELETE:
2906 if (IsWritable())
2908 if (! HasTextSelection())
2910 if (bControl)
2912 m_ptSelectionViewPosStart = GetCaretViewPosition();
2913 MoveCaretWordRight();
2914 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2916 else
2918 if (! MoveCaretRight())
2919 break;
2920 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2921 MoveCaretLeft();
2922 m_ptSelectionViewPosStart = GetCaretViewPosition();
2925 RemoveSelectedText();
2927 break;
2929 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2932 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2934 const int nClickedLine = GetButtonEventLineIndex(point);
2935 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2937 POINT ptCaretPos;
2938 ptCaretPos.y = nClickedLine;
2939 LONG xpos = point.x - GetMarginWidth();
2940 LONG xpos2 = xpos / GetCharWidth();
2941 xpos2 += m_nOffsetChar;
2942 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
2943 xpos2++;
2944 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
2945 SetCaretAndGoalPosition(ptCaretPos);
2947 if (nFlags & MK_SHIFT)
2948 AdjustSelection(MOVERIGHT);
2949 else
2951 ClearSelection();
2952 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
2953 if (point.x < GetMarginWidth())
2955 // select the whole line
2956 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
2957 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
2961 UpdateViewsCaretPosition();
2962 Invalidate();
2965 CView::OnLButtonDown(nFlags, point);
2968 enum ECharGroup { // ordered by priority low-to-hi
2969 CHG_UNKNOWN,
2970 CHG_CONTROL, // x00-x08, x0a-x1f
2971 CHG_WHITESPACE, // space tab
2972 CHG_PUNCTUATION, // 0x21-2f, x3a-x40, x5b-x60, x7b-x7f .,:;!?(){}[]/\<> ...
2973 CHG_WORDLETTER, // alpha num _ (others)
2976 ECharGroup GetCharGroup(wchar_t zChar)
2978 if (zChar == ' ' || zChar == '\t' )
2980 return CHG_WHITESPACE;
2982 if (zChar < 0x20)
2984 return CHG_CONTROL;
2986 if ((zChar >= 0x21 && zChar <= 0x2f)
2987 || (zChar >= 0x3a && zChar <= 0x40)
2988 || (zChar >= 0x5b && zChar <= 0x5e)
2989 || (zChar == 0x60)
2990 || (zChar >= 0x7b && zChar <= 0x7f))
2992 return CHG_PUNCTUATION;
2994 return CHG_WORDLETTER;
2997 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
2999 if (m_pViewData == 0) {
3000 CView::OnLButtonDblClk(nFlags, point);
3001 return;
3004 const int nClickedLine = GetButtonEventLineIndex(point);
3005 if ( nClickedLine < 0)
3006 return;
3007 int nViewLine = GetViewLineForScreen(nClickedLine);
3008 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3010 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3012 if((m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)||
3013 (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO))
3015 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3016 int screenLine = FindViewLineNumber(movedindex);
3017 int nTop = screenLine - GetScreenLines()/2;
3018 if (nTop < 0)
3019 nTop = 0;
3020 ScrollAllToLine(nTop);
3021 // find and select the whole moved block
3022 int startSel = movedindex;
3023 int endSel = movedindex;
3024 while ((startSel > 0) && ((m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_TO)))
3025 startSel--;
3026 startSel++;
3027 while ((endSel < GetLineCount()) && ((m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_TO)))
3028 endSel++;
3029 endSel--;
3030 m_pOtherView->SetupSelection(startSel, endSel);
3031 return CView::OnLButtonDblClk(nFlags, point);
3035 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3037 // a double click on a marker expands the hidden text
3038 int i = nViewLine;
3039 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3041 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3042 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3043 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3044 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3045 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3046 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3047 i++;
3049 BuildAllScreen2ViewVector();
3050 if (m_pwndLeft)
3051 m_pwndLeft->Invalidate();
3052 if (m_pwndRight)
3053 m_pwndRight->Invalidate();
3054 if (m_pwndBottom)
3055 m_pwndBottom->Invalidate();
3057 else
3059 POINT ptCaretPos;
3060 ptCaretPos.y = nClickedLine;
3061 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3062 SetCaretPosition(ptCaretPos);
3063 ClearSelection();
3065 POINT ptViewCarret = GetCaretViewPosition();
3066 nViewLine = ptViewCarret.y;
3067 if (nViewLine >= GetViewCount())
3068 return;
3069 CString sLine = GetViewLine(nViewLine);
3070 int nLineLength = sLine.GetLength();
3071 int nBasePos = ptViewCarret.x;
3072 // get target char group
3073 ECharGroup eLeft = CHG_UNKNOWN;
3074 if (nBasePos > 0)
3076 eLeft = GetCharGroup(sLine[nBasePos-1]);
3078 ECharGroup eRight = CHG_UNKNOWN;
3079 if (nBasePos < nLineLength)
3081 eRight = GetCharGroup(sLine[nBasePos]);
3083 ECharGroup eTarget = max(eRight, eLeft);
3084 // find left margin
3085 int nLeft = nBasePos;
3086 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3088 nLeft--;
3090 // get right margin
3091 int nRight = nBasePos;
3092 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3094 nRight++;
3096 // set selection
3097 m_ptSelectionViewPosStart.x = nLeft;
3098 m_ptSelectionViewPosStart.y = nViewLine;
3099 m_ptSelectionViewPosEnd.x = nRight;
3100 m_ptSelectionViewPosEnd.y = nViewLine;
3101 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3102 SetupAllViewSelection(nViewLine, nViewLine);
3103 // set caret
3104 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3105 UpdateViewsCaretPosition();
3106 UpdateGoalPos();
3108 // set mark word
3109 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3110 int nMarkWidth = max(nRight - nLeft, 0);
3111 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3112 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3114 m_sMarkedWord.Empty();
3117 if (m_pwndLeft)
3118 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3119 if (m_pwndRight)
3120 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3121 if (m_pwndBottom)
3122 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3124 Invalidate();
3125 if (m_pwndLocator)
3126 m_pwndLocator->Invalidate();
3129 CView::OnLButtonDblClk(nFlags, point);
3132 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3134 const int nClickedLine = GetButtonEventLineIndex(point);
3135 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3137 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3139 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3140 if (pidl)
3142 SHOpenFolderAndSelectItems(pidl,0,0,0);
3143 CoTaskMemFree((LPVOID)pidl);
3146 return;
3148 POINT ptCaretPos;
3149 ptCaretPos.y = nClickedLine;
3150 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3151 SetCaretAndGoalPosition(ptCaretPos);
3152 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3153 if (m_pwndLeft)
3154 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3155 if (m_pwndRight)
3156 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3157 if (m_pwndBottom)
3158 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3159 ClearSelection();
3160 m_ptSelectionViewPosStart.x = 0;
3161 m_ptSelectionViewPosStart.y = nClickedLine;
3162 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3163 m_ptSelectionViewPosEnd.y = nClickedLine;
3164 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3165 UpdateViewsCaretPosition();
3166 Invalidate();
3167 if (m_pwndLocator)
3168 m_pwndLocator->Invalidate();
3171 void CBaseView::OnEditCopy()
3173 CString sCopyData = GetSelectedText();
3175 if (!sCopyData.IsEmpty())
3177 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3181 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3183 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3185 --m_pMainFrame->m_nMoveMovesToIgnore;
3186 CView::OnMouseMove(nFlags, point);
3187 return;
3189 int nMouseLine = GetButtonEventLineIndex(point);
3190 if (nMouseLine < -1)
3191 nMouseLine = -1;
3192 m_mouseInMargin = point.x < GetMarginWidth();
3194 ShowDiffLines(nMouseLine);
3196 KillTimer(IDT_SCROLLTIMER);
3197 if (nFlags & MK_LBUTTON)
3199 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3200 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3201 if (saveMouseLine < 0)
3202 return;
3203 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3204 if (HasSelection() &&
3205 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3207 POINT ptCaretPos = {charIndex, nMouseLine};
3208 SetCaretAndGoalPosition(ptCaretPos);
3209 AdjustSelection(MOVERIGHT);
3210 Invalidate();
3211 UpdateWindow();
3213 if (nMouseLine < m_nTopLine)
3215 ScrollAllToLine(m_nTopLine-1, TRUE);
3216 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3218 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3220 ScrollAllToLine(m_nTopLine+1, TRUE);
3221 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3223 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3225 ScrollAllSide(-1);
3226 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3228 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3230 ScrollAllSide(1);
3231 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3233 SetCapture();
3237 CView::OnMouseMove(nFlags, point);
3240 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3242 ShowDiffLines(-1);
3243 ReleaseCapture();
3244 KillTimer(IDT_SCROLLTIMER);
3246 __super::OnLButtonUp(nFlags, point);
3249 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3251 if (nIDEvent == IDT_SCROLLTIMER)
3253 POINT point;
3254 GetCursorPos(&point);
3255 ScreenToClient(&point);
3256 int nMouseLine = GetButtonEventLineIndex(point);
3257 if (nMouseLine < -1)
3259 nMouseLine = -1;
3261 if (GetKeyState(VK_LBUTTON)&0x8000)
3263 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3264 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3265 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3266 if (nMouseLine < m_nTopLine)
3268 ScrollAllToLine(m_nTopLine-1, TRUE);
3269 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3271 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3273 ScrollAllToLine(m_nTopLine+1, TRUE);
3274 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3276 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3278 ScrollAllSide(-1);
3279 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3281 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3283 ScrollAllSide(1);
3284 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3290 CView::OnTimer(nIDEvent);
3293 void CBaseView::ShowDiffLines(int nLine)
3295 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3297 m_pwndLineDiffBar->ShowLines(nLine);
3298 nLine = -1;
3299 m_nMouseLine = nLine;
3300 return;
3303 if ((!m_pwndRight)||(!m_pwndLeft))
3304 return;
3305 if(m_pMainFrame->m_bOneWay)
3306 return;
3308 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3309 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3311 if (nLine < 0)
3312 return;
3314 if (nLine != m_nMouseLine)
3316 if (nLine >= GetLineCount())
3317 nLine = -1;
3318 m_nMouseLine = nLine;
3319 m_pwndLineDiffBar->ShowLines(nLine);
3321 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3324 const viewdata& CBaseView::GetEmptyLineData()
3326 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN, -1);
3327 return emptyLine;
3330 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3332 for (int i = 0; i < nCount; i++)
3334 InsertViewData(nFirstView, GetEmptyLineData());
3339 void CBaseView::UpdateCaret()
3341 POINT ptCaretPos = GetCaretPosition();
3342 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3343 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3344 SetCaretPosition(ptCaretPos);
3346 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3348 if (m_bFocused &&
3349 ptCaretPos.y >= m_nTopLine &&
3350 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3351 nCaretOffset >= m_nOffsetChar &&
3352 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3354 CreateSolidCaret(2, GetLineHeight());
3355 SetCaretPos(TextToClient(ptCaretPos));
3356 ShowCaret();
3358 else
3360 HideCaret();
3364 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3366 POINT ptViewPos;
3367 ptViewPos.x = pt.x;
3369 int nSubLine = GetSubLineOffset(pt.y);
3370 if (nSubLine > 0)
3372 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3374 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3378 ptViewPos.y = GetViewLineForScreen(pt.y);
3379 return ptViewPos;
3382 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3384 POINT ptPos;
3385 int nViewLineLenLeft = GetViewLineLength(pt.y);
3386 ptPos.x = min(nViewLineLenLeft, pt.x);
3387 ptPos.y = FindScreenLineForViewLine(pt.y);
3388 if (GetViewLineForScreen(ptPos.y) != pt.y )
3390 ptPos.x = 0;
3392 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3394 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3395 while (nSubLineLength < ptPos.x)
3397 ptPos.x -= nSubLineLength;
3398 nViewLineLenLeft -= nSubLineLength;
3399 ptPos.y++;
3400 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3402 // last pos of non last sub-line go to start of next screen line
3403 // Note: while this works correctly, it's not what a user might expect:
3404 // cursor-right when the caret is before the last char of a wrapped line
3405 // now moves the caret to the next line. But users expect the caret to
3406 // move to the right of the last char instead, and with another cursor-right
3407 // keystroke to move the caret to the next line.
3408 // Basically, this would require to handle two caret positions for the same
3409 // logical position in the line string (one on the last position of the first line,
3410 // one on the first position of the new line. For non-wrapped lines this works
3411 // because there's an 'invisible' newline char at the end of the first line.
3412 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3414 ptPos.x = 0;
3415 ptPos.y++;
3419 return ptPos;
3423 void CBaseView::EnsureCaretVisible()
3425 POINT ptCaretPos = GetCaretPosition();
3426 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3428 if (ptCaretPos.y < m_nTopLine)
3429 ScrollAllToLine(ptCaretPos.y);
3430 int screnLines = GetScreenLines();
3431 if (screnLines)
3433 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3434 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3435 if (nCaretOffset < m_nOffsetChar)
3436 ScrollAllToChar(nCaretOffset);
3437 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3438 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3442 int CBaseView::CalculateActualOffset(const POINT& point)
3444 int nLineIndex = point.y;
3445 int nCharIndex = point.x;
3446 ASSERT(nCharIndex >= 0);
3447 CString sLine = GetLineChars(nLineIndex);
3448 int nLineLength = sLine.GetLength();
3449 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3452 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3454 int nLength = GetLineLength(nLineIndex);
3455 int nSubLine = GetSubLineOffset(nLineIndex);
3456 if (nSubLine>=0)
3458 int nViewLine = GetViewLineForScreen(nLineIndex);
3459 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3461 int nMultilineCount = CountMultiLines(nViewLine);
3462 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3464 nLength--;
3468 CString Line = GetLineChars(nLineIndex);
3469 int nIndex = 0;
3470 int nOffset = 0;
3471 int nTabSize = GetTabSize();
3472 while (nOffset < nActualOffset && nIndex < nLength)
3474 if (Line.GetAt(nIndex) == _T('\t'))
3475 nOffset += (nTabSize - nOffset % nTabSize);
3476 else
3477 ++nOffset;
3478 ++nIndex;
3480 return nIndex;
3483 POINT CBaseView::TextToClient(const POINT& point)
3485 POINT pt;
3486 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3487 pt.y = nOffsetScreenLine * GetLineHeight();
3488 pt.x = CalculateActualOffset(point);
3490 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3491 CDC * pDC = GetDC();
3492 if (pDC)
3494 pDC->SelectObject(GetFont()); // is this right font ?
3495 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3496 CString sLine = GetLineChars(nScreenLine);
3497 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3498 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3499 ReleaseDC(pDC);
3500 } else {
3501 nLeft += pt.x * GetCharWidth();
3504 pt.x = nLeft;
3505 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3506 return pt;
3509 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3511 CView::OnChar(nChar, nRepCnt, nFlags);
3513 if (IsReadonly())
3514 return;
3516 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3517 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3519 return;
3522 if (!m_pViewData) // no data - nothing to do
3523 return;
3525 if (nChar == VK_F16)
3527 // generated by a ctrl+backspace - ignore.
3529 else if ((nChar > 31)||(nChar == VK_TAB))
3531 ResetUndoStep();
3532 RemoveSelectedText();
3533 POINT ptCaretViewPos = GetCaretViewPosition();
3534 int nViewLine = ptCaretViewPos.y;
3535 if ((nViewLine==0)&&(GetViewCount()==0))
3536 OnChar(VK_RETURN, 0, 0);
3537 viewdata lineData = GetViewData(nViewLine);
3538 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3539 if (IsStateEmpty(lineData.state))
3541 // if not last line set EOL
3542 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3544 if (!IsViewLineEmpty(nCheckViewLine))
3546 lineData.ending = lineendings;
3547 break;
3550 // make sure previous (non empty) line have EOL set
3551 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3553 if (!IsViewLineEmpty(nCheckViewLine))
3555 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3557 SetViewLineEnding(nCheckViewLine, lineendings);
3559 break;
3563 lineData.state = DIFFSTATE_EDITED;
3564 bool bNeedRenumber = false;
3565 if (lineData.linenumber == -1)
3567 lineData.linenumber = 0;
3568 bNeedRenumber = true;
3570 SetViewData(nViewLine, lineData);
3571 SaveUndoStep();
3572 BuildAllScreen2ViewVector(nViewLine);
3573 if (bNeedRenumber)
3575 UpdateViewLineNumbers();
3577 MoveCaretRight();
3578 UpdateGoalPos();
3580 else if (nChar == 10)
3582 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3583 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3584 EOL newEOL = EOL_CRLF;
3585 switch (eol)
3587 case EOL_CRLF:
3588 newEOL = EOL_CR;
3589 break;
3590 case EOL_CR:
3591 newEOL = EOL_LF;
3592 break;
3593 case EOL_LF:
3594 newEOL = EOL_CRLF;
3595 break;
3597 if (eol==EOL_NOENDING || eol==newEOL)
3598 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3599 // to add EOL on newly edited empty line hit enter
3600 // don't store into UNDO if no change happened
3601 // and don't mark file as modified
3602 return;
3603 AddUndoViewLine(nViewLine);
3604 m_pViewData->SetLineEnding(nViewLine, newEOL);
3605 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3606 UpdateGoalPos();
3608 else if (nChar == VK_RETURN)
3610 // insert a new, fresh and empty line below the cursor
3611 RemoveSelectedText();
3613 CUndo::GetInstance().BeginGrouping();
3615 POINT ptCaretViewPos = GetCaretViewPosition();
3616 int nViewLine = ptCaretViewPos.y;
3617 int nLeft = ptCaretViewPos.x;
3618 CString sLine = GetViewLineChars(nViewLine);
3619 CString sLineLeft = sLine.Left(nLeft);
3620 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3621 EOL eOriginalEnding = EOL_AUTOLINE;
3622 if (m_pViewData->GetCount() > nViewLine)
3623 eOriginalEnding = GetViewLineEnding(nViewLine);
3625 if (!sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3627 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3628 SetViewData(nViewLine, newFirstLine);
3631 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3632 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN, -1);
3633 InsertViewData(nInsertLine, newLastLine);
3634 SaveUndoStep();
3636 // adds new line everywhere except me
3637 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3639 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3641 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3643 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3645 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3647 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3649 SaveUndoStep();
3651 UpdateViewLineNumbers();
3652 SaveUndoStep();
3653 CUndo::GetInstance().EndGrouping();
3655 BuildAllScreen2ViewVector();
3656 // move the cursor to the new line
3657 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3658 SetCaretAndGoalViewPosition(ptCaretViewPos);
3660 else
3661 return; // Unknown control character -- ignore it.
3662 ClearSelection();
3663 EnsureCaretVisible();
3664 UpdateCaret();
3665 SetModified(true);
3666 Invalidate(FALSE);
3669 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3671 ResetUndoStep();
3672 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3673 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3674 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3675 SaveUndoStep();
3676 RecalcAllVertScrollBars();
3677 Invalidate(FALSE);
3680 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3682 if (m_pViewData == NULL)
3683 return;
3684 int viewLine = nViewLineIndex;
3685 EOL ending = m_pViewData->GetLineEnding(viewLine);
3686 if (ending == EOL_NOENDING)
3688 ending = lineendings;
3690 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN, -1);
3691 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3693 CString sPartLine = GetViewLineChars(nViewLineIndex);
3694 int nPosx = GetCaretPosition().x; // should be view pos ?
3695 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3696 sPartLine = sPartLine.Mid(nPosx);
3697 newLine.sLine = sPartLine;
3699 m_pViewData->InsertData(viewLine+1, newLine);
3700 BuildAllScreen2ViewVector();
3703 void CBaseView::RemoveSelectedText()
3705 if (m_pViewData == NULL)
3706 return;
3707 if (!HasTextSelection())
3708 return;
3710 // fix selection if starts or ends on empty line
3711 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3712 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3715 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3716 SetCaretViewPosition(m_ptSelectionViewPosStart);
3717 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3720 m_ptSelectionViewPosStart = GetCaretViewPosition();
3721 if (!HasTextSelection())
3723 ClearSelection();
3724 return;
3727 // We want to undo the insertion in a single step.
3728 ResetUndoStep();
3729 CUndo::GetInstance().BeginGrouping();
3731 // combine first and last line
3732 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3733 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3734 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3735 oFirstLine.ending = oLastLine.ending;
3736 oFirstLine.state = DIFFSTATE_EDITED;
3737 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3739 // clean up middle lines if any
3740 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3742 viewdata oEmptyLine = GetEmptyLineData();
3743 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3745 SetViewData(nViewLine, oEmptyLine);
3747 SaveUndoStep();
3749 if (CleanEmptyLines())
3751 BuildAllScreen2ViewVector(); // schedule full rebuild
3753 SaveUndoStep();
3754 UpdateViewLineNumbers();
3757 SaveUndoStep();
3758 CUndo::GetInstance().EndGrouping();
3760 SetModified();
3761 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3762 SetCaretViewPosition(m_ptSelectionViewPosStart);
3763 UpdateGoalPos();
3764 ClearSelection();
3765 UpdateCaret();
3766 EnsureCaretVisible();
3767 Invalidate(FALSE);
3770 void CBaseView::PasteText()
3772 if (!OpenClipboard())
3773 return;
3775 CString sClipboardText;
3776 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3777 if (hglb)
3779 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3780 sClipboardText = CString(lpstr);
3781 GlobalUnlock(hglb);
3783 hglb = GetClipboardData(CF_UNICODETEXT);
3784 if (hglb)
3786 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3787 sClipboardText = lpstr;
3788 GlobalUnlock(hglb);
3790 CloseClipboard();
3792 if (sClipboardText.IsEmpty())
3793 return;
3795 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3796 sClipboardText.Replace('\n', '\r');
3798 ResetUndoStep();
3800 POINT ptCaretViewPos = GetCaretViewPosition();
3801 int nLeft = ptCaretViewPos.x;
3802 int nViewLine = ptCaretViewPos.y;
3804 if ((nViewLine==0)&&(GetViewCount()==0))
3805 OnChar(VK_RETURN, 0, 0);
3807 std::vector<CString> lines;
3808 int nStart = 0;
3809 int nEolPos = 0;
3810 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3812 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3813 lines.push_back(sLine);
3814 nEolPos++;
3815 nStart = nEolPos;
3817 CString sLine = sClipboardText.Mid(nStart);
3818 lines.push_back(sLine);
3820 int nLinesToPaste = (int)lines.size();
3821 if (nLinesToPaste > 1)
3823 // multiline text
3825 // We want to undo the multiline insertion in a single step.
3826 CUndo::GetInstance().BeginGrouping();
3828 sLine = GetViewLineChars(nViewLine);
3829 CString sLineLeft = sLine.Left(nLeft);
3830 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3831 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3832 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3833 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3835 newLine.sLine = sLineLeft + lines[0];
3836 SetViewData(nViewLine, newLine);
3839 int nInsertLine = nViewLine;
3840 for (int i = 1; i < nLinesToPaste-1; i++)
3842 newLine.sLine = lines[i];
3843 InsertViewData(++nInsertLine, newLine);
3845 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3846 newLine.ending = eOriginalEnding;
3847 InsertViewData(++nInsertLine, newLine);
3849 SaveUndoStep();
3851 // adds new lines everywhere except me
3852 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3854 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3856 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3858 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3860 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3862 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3864 SaveUndoStep();
3866 UpdateViewLineNumbers();
3867 CUndo::GetInstance().EndGrouping();
3869 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3871 else
3873 // single line text - just insert it
3874 sLine = GetViewLineChars(nViewLine);
3875 sLine.Insert(nLeft, sClipboardText);
3876 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
3877 SetViewLine(nViewLine, sLine);
3878 SetViewState(nViewLine, DIFFSTATE_EDITED);
3879 SaveUndoStep();
3882 SetModified();
3883 RefreshViews();
3884 BuildAllScreen2ViewVector();
3885 UpdateCaretViewPosition(ptCaretViewPos);
3888 void CBaseView::OnCaretDown()
3890 POINT ptCaretPos = GetCaretPosition();
3891 int nLine = ptCaretPos.y;
3892 int nNextLine = nLine + 1;
3893 if (nNextLine >= GetLineCount()) // already at last line
3895 return;
3898 POINT ptCaretViewPos = GetCaretViewPosition();
3899 int nViewLine = ptCaretViewPos.y;
3900 int nNextViewLine = GetViewLineForScreen(nNextLine);
3901 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3903 // find next suitable screen line
3904 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3906 nNextLine++;
3907 if (nNextLine >= GetLineCount())
3909 return;
3911 nNextViewLine = GetViewLineForScreen(nNextLine);
3914 ptCaretPos.y = nNextLine;
3915 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3916 SetCaretPosition(ptCaretPos);
3917 OnCaretMove(MOVELEFT);
3918 ShowDiffLines(ptCaretPos.y);
3921 bool CBaseView::MoveCaretLeft()
3923 POINT ptCaretViewPos = GetCaretViewPosition();
3925 //int nViewLine = ptCaretViewPos.y;
3926 if (ptCaretViewPos.x == 0)
3928 int nPrevLine = GetCaretPosition().y;
3929 int nPrevViewLine;
3930 do {
3931 nPrevLine--;
3932 if (nPrevLine < 0)
3934 return false;
3936 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3937 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
3938 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
3939 ShowDiffLines(nPrevLine);
3941 else
3942 --ptCaretViewPos.x;
3944 SetCaretAndGoalViewPosition(ptCaretViewPos);
3945 return true;
3948 bool CBaseView::MoveCaretRight()
3950 POINT ptCaretViewPos = GetCaretViewPosition();
3952 int nViewLine = ptCaretViewPos.y;
3953 int nViewLineLen = GetViewLineLength(nViewLine);
3954 if (ptCaretViewPos.x >= nViewLineLen)
3956 int nNextLine = GetCaretPosition().y;
3957 int nNextViewLine;
3958 do {
3959 nNextLine++;
3960 if (nNextLine >= GetLineCount())
3962 return false;
3964 nNextViewLine = GetViewLineForScreen(nNextLine);
3965 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
3966 ptCaretViewPos.y = nNextViewLine;
3967 ptCaretViewPos.x = 0;
3968 ShowDiffLines(nNextLine);
3970 else
3971 ++ptCaretViewPos.x;
3973 SetCaretAndGoalViewPosition(ptCaretViewPos);
3974 return true;
3977 void CBaseView::UpdateGoalPos()
3979 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
3982 void CBaseView::OnCaretLeft()
3984 MoveCaretLeft();
3985 OnCaretMove(MOVELEFT);
3988 void CBaseView::OnCaretRight()
3990 MoveCaretRight();
3991 OnCaretMove(MOVERIGHT);
3994 void CBaseView::OnCaretUp()
3996 POINT ptCaretPos = GetCaretPosition();
3997 int nLine = ptCaretPos.y;
3998 if (nLine <= 0) // already at first line
4000 return;
4002 int nPrevLine = nLine - 1;
4004 POINT ptCaretViewPos = GetCaretViewPosition();
4005 int nViewLine = ptCaretViewPos.y;
4006 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4007 if (nPrevViewLine != nViewLine) // not on same view line
4009 // find previous suitable screen line
4010 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4012 if (nPrevLine <= 0)
4014 return;
4016 nPrevLine--;
4017 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4020 ptCaretPos.y = nPrevLine;
4021 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4022 SetCaretPosition(ptCaretPos);
4023 OnCaretMove(MOVELEFT);
4024 ShowDiffLines(ptCaretPos.y);
4027 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4029 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
4032 bool CBaseView::IsCaretAtWordBoundary()
4034 POINT ptViewCaret = GetCaretViewPosition();
4035 CString line = GetViewLineChars(ptViewCaret.y);
4036 if (line.IsEmpty())
4037 return false; // no boundary at the empty lines
4038 if (ptViewCaret.x == 0)
4039 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4040 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4041 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4042 return
4043 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4044 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4047 void CBaseView::UpdateViewsCaretPosition()
4049 POINT ptCaretPos = GetCaretPosition();
4050 if (m_pwndBottom && m_pwndBottom!=this)
4051 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4052 if (m_pwndLeft && m_pwndLeft!=this)
4053 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4054 if (m_pwndRight && m_pwndRight!=this)
4055 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4058 void CBaseView::OnCaretWordleft()
4060 MoveCaretWordLeft();
4061 OnCaretMove(MOVELEFT);
4064 void CBaseView::OnCaretWordright()
4066 MoveCaretWordRight();
4067 OnCaretMove(MOVERIGHT);
4070 void CBaseView::MoveCaretWordLeft()
4072 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4077 void CBaseView::MoveCaretWordRight()
4079 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4084 void CBaseView::ClearCurrentSelection()
4086 m_ptSelectionViewPosStart = GetCaretViewPosition();
4087 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4088 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4089 m_nSelViewBlockStart = -1;
4090 m_nSelViewBlockEnd = -1;
4091 Invalidate(FALSE);
4094 void CBaseView::ClearSelection()
4096 if (m_pwndLeft)
4097 m_pwndLeft->ClearCurrentSelection();
4098 if (m_pwndRight)
4099 m_pwndRight->ClearCurrentSelection();
4100 if (m_pwndBottom)
4101 m_pwndBottom->ClearCurrentSelection();
4104 void CBaseView::AdjustSelection(bool bMoveLeft)
4106 POINT ptCaretViewPos = GetCaretViewPosition();
4107 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4109 // select all have been used recently update origin
4110 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4112 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4113 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4115 m_ptSelectionViewPosStart = ptCaretViewPos;
4116 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4118 else
4120 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4121 m_ptSelectionViewPosEnd = ptCaretViewPos;
4124 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4126 Invalidate(FALSE);
4129 void CBaseView::OnEditCut()
4131 if (IsWritable())
4133 OnEditCopy();
4134 RemoveSelectedText();
4138 void CBaseView::OnEditPaste()
4140 if (IsWritable())
4142 CUndo::GetInstance().BeginGrouping();
4143 RemoveSelectedText();
4144 PasteText();
4145 CUndo::GetInstance().EndGrouping();
4149 void CBaseView::DeleteFonts()
4151 for (int i=0; i<fontsCount; i++)
4153 if (m_apFonts[i] != NULL)
4155 m_apFonts[i]->DeleteObject();
4156 delete m_apFonts[i];
4157 m_apFonts[i] = NULL;
4162 void CBaseView::OnCaretMove(bool bMoveLeft)
4164 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4165 OnCaretMove(bMoveLeft, bShift);
4168 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4170 if(isShiftPressed)
4171 AdjustSelection(bMoveLeft);
4172 else
4173 ClearSelection();
4174 EnsureCaretVisible();
4175 UpdateCaret();
4178 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4180 AddCutCopyAndPaste(popup);
4183 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4185 popup.AppendMenu(MF_SEPARATOR, NULL);
4186 CString temp;
4187 temp.LoadString(IDS_EDIT_COPY);
4188 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4189 if (IsWritable())
4191 temp.LoadString(IDS_EDIT_CUT);
4192 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4193 temp.LoadString(IDS_EDIT_PASTE);
4194 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4198 void CBaseView::CompensateForKeyboard(CPoint& point)
4200 // if the context menu is invoked through the keyboard, we have to use
4201 // a calculated position on where to anchor the menu on
4202 if (ArePointsSame(point, SetupPoint(-1, -1)))
4204 CRect rect;
4205 GetWindowRect(&rect);
4206 point = rect.CenterPoint();
4210 HICON CBaseView::LoadIcon(WORD iconId)
4212 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4213 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4214 return (HICON)icon;
4217 void CBaseView::ReleaseBitmap()
4219 if (m_pCacheBitmap != NULL)
4221 m_pCacheBitmap->DeleteObject();
4222 delete m_pCacheBitmap;
4223 m_pCacheBitmap = NULL;
4227 void CBaseView::BuildMarkedWordArray()
4229 int lineCount = GetLineCount();
4230 m_arMarkedWordLines.clear();
4231 m_arMarkedWordLines.reserve(lineCount);
4232 bool bDoit = !m_sMarkedWord.IsEmpty();
4233 for (int i = 0; i < lineCount; ++i)
4235 if (bDoit)
4237 CString line = GetLineChars(i);
4239 if (!line.IsEmpty())
4241 m_arMarkedWordLines.push_back(line.Find(m_sMarkedWord) != -1);
4243 else
4244 m_arMarkedWordLines.push_back(0);
4246 else
4247 m_arMarkedWordLines.push_back(0);
4251 void CBaseView::BuildFindStringArray()
4253 int lineCount = GetLineCount();
4254 m_arFindStringLines.clear();
4255 m_arFindStringLines.reserve(lineCount);
4256 bool bDoit = !m_sFindText.IsEmpty();
4257 int s = 0;
4258 int e = 0;
4259 for (int i = 0; i < lineCount; ++i)
4261 if (bDoit)
4263 CString line = GetLineChars(i);
4265 if (!line.IsEmpty())
4267 line = line.MakeLower();
4268 m_arFindStringLines.push_back(StringFound(line, SearchNext, s, e));
4270 else
4271 m_arFindStringLines.push_back(0);
4273 else
4274 m_arFindStringLines.push_back(0);
4276 UpdateLocator();
4279 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4281 if (!m_bShowInlineDiff)
4282 return false;
4283 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4284 return false;
4286 CString sLine = GetViewLineChars(nViewLine);
4287 if (sLine.IsEmpty())
4288 return false;
4290 CheckOtherView();
4291 if (!m_pOtherViewData)
4292 return false;
4294 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4295 if (sDiffLine.IsEmpty())
4296 return false;
4298 CString sLineExp = ExpandChars(sLine);
4299 CString sDiffLineExp = ExpandChars(sDiffLine);
4300 svn_diff_t * diff = NULL;
4301 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4302 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4303 return false;
4305 size_t lineoffset = 0;
4306 size_t position = 0;
4307 while (diff)
4309 apr_off_t len = diff->original_length;
4310 size_t oldpos = position;
4312 for (apr_off_t i = 0; i < len; ++i)
4314 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4315 lineoffset++;
4318 if (diff->type == svn_diff__type_diff_modified)
4320 inlineDiffPos p;
4321 p.start = oldpos;
4322 p.end = position;
4323 positions.push_back(p);
4326 diff = diff->next;
4329 return !positions.empty();
4332 void CBaseView::OnNavigateNextinlinediff()
4334 int nX;
4335 if (GetNextInlineDiff(nX))
4337 POINT ptCaretViewPos = GetCaretViewPosition();
4338 ptCaretViewPos.x = nX;
4339 SetCaretAndGoalViewPosition(ptCaretViewPos);
4340 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4341 EnsureCaretVisible();
4345 void CBaseView::OnNavigatePrevinlinediff()
4347 int nX;
4348 if (GetPrevInlineDiff(nX))
4350 POINT ptCaretViewPos = GetCaretViewPosition();
4351 ptCaretViewPos.x = nX;
4352 SetCaretAndGoalViewPosition(ptCaretViewPos);
4353 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4354 EnsureCaretVisible();
4358 bool CBaseView::HasNextInlineDiff()
4360 int nPos;
4361 return GetNextInlineDiff(nPos);
4364 bool CBaseView::GetNextInlineDiff(int & nPos)
4366 POINT ptCaretViewPos = GetCaretViewPosition();
4367 std::vector<inlineDiffPos> positions;
4368 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4370 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4372 if (it->start > ptCaretViewPos.x)
4374 nPos = (LONG)it->start;
4375 return true;
4377 if (it->end > ptCaretViewPos.x)
4379 nPos = (LONG)it->end;
4380 return true;
4384 return false;
4387 bool CBaseView::HasPrevInlineDiff()
4389 int nPos;
4390 return GetPrevInlineDiff(nPos);
4393 bool CBaseView::GetPrevInlineDiff(int & nPos)
4395 POINT ptCaretViewPos = GetCaretViewPosition();
4396 std::vector<inlineDiffPos> positions;
4397 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4399 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4401 if ( it->end < ptCaretViewPos.x)
4403 nPos = (LONG)it->end;
4404 return true;
4406 if ( it->start < ptCaretViewPos.x)
4408 nPos = (LONG)it->start;
4409 return true;
4413 return false;
4416 CBaseView * CBaseView::GetFirstGoodView()
4418 if (IsViewGood(m_pwndLeft))
4419 return m_pwndLeft;
4420 if (IsViewGood(m_pwndRight))
4421 return m_pwndRight;
4422 if (IsViewGood(m_pwndBottom))
4423 return m_pwndBottom;
4424 return NULL;
4427 void CBaseView::BuildAllScreen2ViewVector()
4429 CBaseView * p_pwndView = GetFirstGoodView();
4430 if (p_pwndView)
4432 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4436 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4438 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4441 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4443 CBaseView * p_pwndView = GetFirstGoodView();
4444 if (p_pwndView)
4446 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4450 void CBaseView::UpdateViewLineNumbers()
4452 int nLineNumber = 0;
4453 int nViewLineCount = GetViewCount();
4454 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4456 int oldLine = (int)GetViewLineNumber(nViewLine);
4457 if (oldLine >= 0)
4458 SetViewLineNumber(nViewLine, nLineNumber++);
4460 m_nDigits = 0;
4463 int CBaseView::CleanEmptyLines()
4465 int nRemovedCount = 0;
4466 int nViewLineCount = GetViewCount();
4467 bool bCheckLeft = IsViewGood(m_pwndLeft);
4468 bool bCheckRight = IsViewGood(m_pwndRight);
4469 bool bCheckBottom = IsViewGood(m_pwndBottom);
4470 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4472 bool bAllEmpty = true;
4473 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4474 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4475 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4476 if (bAllEmpty)
4478 if (bCheckLeft)
4480 m_pwndLeft->RemoveViewData(nViewLine);
4482 if (bCheckRight)
4484 m_pwndRight->RemoveViewData(nViewLine);
4486 if (bCheckBottom)
4488 m_pwndBottom->RemoveViewData(nViewLine);
4490 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4492 SaveUndoStep();
4494 nViewLineCount--;
4495 nRemovedCount++;
4496 continue;
4498 nViewLine++;
4500 return nRemovedCount;
4503 int CBaseView::FindScreenLineForViewLine( int viewLine )
4505 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4508 int CBaseView::CountMultiLines( int nViewLine )
4510 if (m_ScreenedViewLine.empty())
4511 return 0; // in case the view is completely empty
4513 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4515 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4517 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4520 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4522 TScreenedViewLine oScreenedLine;
4523 // tokenize string
4524 int prevpos = 0;
4525 int pos = 0;
4526 while ((pos = multiline.Find('\n', pos)) >= 0)
4528 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4529 pos++;
4530 prevpos = pos;
4532 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4533 oScreenedLine.bSublinesSet = true;
4534 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4536 return CountMultiLines(nViewLine);
4539 /// prepare inline diff cache
4540 LineColors & CBaseView::GetLineColors(int nViewLine)
4542 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4544 if (m_bWhitespaceInlineDiffs)
4546 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4547 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4549 else
4551 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4552 return m_ScreenedViewLine[nViewLine].lineColors;
4555 LineColors oLineColors;
4556 // set main line color
4557 COLORREF crBkgnd, crText;
4558 DiffStates diffState = m_pViewData->GetState(nViewLine);
4559 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4560 oLineColors.SetColor(0, crText, crBkgnd);
4562 do {
4563 if (!m_bShowInlineDiff)
4564 break;
4566 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4567 break;
4569 CString sLine = GetViewLineChars(nViewLine);
4570 if (sLine.IsEmpty())
4571 break;
4572 if (!m_pOtherView)
4573 break;
4575 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4576 if (sDiffLine.IsEmpty())
4577 break;
4579 svn_diff_t * diff = NULL;
4580 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4581 break;
4582 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4583 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4584 break;
4586 int lineoffset = 0;
4587 int nTextStartOffset = 0;
4588 std::map<int, COLORREF> removedPositions;
4589 while (diff)
4591 apr_off_t len = diff->original_length;
4593 CString s;
4594 for (int i = 0; i < len; ++i)
4596 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4597 lineoffset++;
4599 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4600 int nTextLength = s.GetLength();
4602 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4603 if ((m_bShowInlineDiff)&&(bInlineDiff))
4605 crBkgnd = InlineViewLineDiffColor(nViewLine);
4607 else
4609 crBkgnd = m_ModifiedBk;
4612 if (len < diff->modified_length)
4614 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4616 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4618 nTextStartOffset += nTextLength;
4619 diff = diff->next;
4621 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4623 oLineColors.AddShotColor(it->first, it->second);
4625 } while (false); // error catch
4627 if (!m_bWhitespaceInlineDiffs)
4629 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4630 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4632 else
4634 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4635 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4638 return GetLineColors(nViewLine);
4641 void CBaseView::OnEditSelectall()
4643 if (m_pViewData == nullptr)
4644 return;
4645 int nLastViewLine = m_pViewData->GetCount()-1;
4646 if (nLastViewLine < 0)
4647 return;
4648 SetupAllViewSelection(0, nLastViewLine);
4650 CString sLine = GetViewLineChars(nLastViewLine);
4651 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4652 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4653 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4655 UpdateWindow();
4658 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4660 FilterWhitespaces(first);
4661 FilterWhitespaces(second);
4664 void CBaseView::FilterWhitespaces(CString& line)
4666 line.Remove(' ');
4667 line.Remove('\t');
4668 line.Remove('\r');
4669 line.Remove('\n');
4672 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4674 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4675 int nEventLine = nLineFromTop + m_nTopLine;
4676 nEventLine--; //we need the index
4677 return nEventLine;
4681 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4683 if (RelayTrippleClick(pMsg))
4684 return TRUE;
4685 return CView::PreTranslateMessage(pMsg);
4689 void CBaseView::ResetUndoStep()
4691 m_AllState.Clear();
4694 void CBaseView::SaveUndoStep()
4696 if (!m_AllState.IsEmpty())
4698 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4700 ResetUndoStep();
4703 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4705 m_pState->addedlines.push_back(index);
4706 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4709 void CBaseView::InsertViewData( int index, const viewdata& data )
4711 m_pState->addedlines.push_back(index);
4712 m_pViewData->InsertData(index, data);
4715 void CBaseView::RemoveViewData( int index )
4717 m_pState->removedlines[index] = m_pViewData->GetData(index);
4718 m_pViewData->RemoveData(index);
4721 void CBaseView::SetViewData( int index, const viewdata& data )
4723 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4724 m_pViewData->SetData(index, data);
4727 void CBaseView::SetViewState( int index, DiffStates state )
4729 m_pState->linestates[index] = m_pViewData->GetState(index);
4730 m_pViewData->SetState(index, state);
4733 void CBaseView::SetViewLine( int index, const CString& sLine )
4735 m_pState->difflines[index] = m_pViewData->GetLine(index);
4736 m_pViewData->SetLine(index, sLine);
4739 void CBaseView::SetViewLineNumber( int index, int linenumber )
4741 int oldLineNumber = m_pViewData->GetLineNumber(index);
4742 if (oldLineNumber != linenumber) {
4743 m_pState->linelines[index] = oldLineNumber;
4744 m_pViewData->SetLineNumber(index, linenumber);
4748 void CBaseView::SetViewLineEnding( int index, EOL ending )
4750 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4751 m_pViewData->SetLineEnding(index, ending);
4755 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4757 if (HasSelection())
4759 start = m_nSelViewBlockStart;
4760 end = m_nSelViewBlockEnd;
4761 return true;
4763 return false;
4766 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4768 RebuildIfNecessary();
4769 if (size() <= screenLine)
4770 return 0;
4771 return m_Screen2View[screenLine].nViewLine;
4774 int CBaseView::Screen2View::size()
4776 RebuildIfNecessary();
4777 return (int)m_Screen2View.size();
4780 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4782 RebuildIfNecessary();
4783 if (size() <= screenLine)
4784 return 0;
4785 return m_Screen2View[screenLine].nViewSubLine;
4788 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4790 RebuildIfNecessary();
4791 return m_Screen2View[screenLine];
4795 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4797 void CBaseView::Screen2View::RebuildIfNecessary()
4799 if (!m_pViewData)
4800 return; // rebuild not necessary
4802 FixScreenedCacheSize(m_pwndLeft);
4803 FixScreenedCacheSize(m_pwndRight);
4804 FixScreenedCacheSize(m_pwndBottom);
4805 if (!m_bFull)
4807 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4809 ResetScreenedViewLineCache(m_pwndLeft, *it);
4810 ResetScreenedViewLineCache(m_pwndRight, *it);
4811 ResetScreenedViewLineCache(m_pwndBottom, *it);
4814 else
4816 ResetScreenedViewLineCache(m_pwndLeft);
4817 ResetScreenedViewLineCache(m_pwndRight);
4818 ResetScreenedViewLineCache(m_pwndBottom);
4820 m_RebuildRanges.clear();
4821 m_bFull = false;
4823 size_t OldSize = m_Screen2View.size();
4824 m_Screen2View.clear();
4825 m_Screen2View.reserve(OldSize); // guess same size
4826 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4828 if (m_pMainFrame->m_bCollapsed)
4830 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4831 ++i;
4832 if (!(i < m_pViewData->GetCount()))
4833 break;
4835 TScreenLineInfo oLineInfo;
4836 oLineInfo.nViewLine = i;
4837 oLineInfo.nViewSubLine = -1; // no wrap
4838 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4840 int nMaxLines = 0;
4841 if (IsLeftViewGood())
4842 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4843 if (IsRightViewGood())
4844 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4845 if (IsBottomViewGood())
4846 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4847 for (int l = 0; l < (nMaxLines-1); ++l)
4849 oLineInfo.nViewSubLine++;
4850 m_Screen2View.push_back(oLineInfo);
4852 oLineInfo.nViewSubLine++;
4854 m_Screen2View.push_back(oLineInfo);
4856 m_pViewData = NULL;
4858 if (IsLeftViewGood())
4859 m_pwndLeft->BuildMarkedWordArray();
4860 if (IsRightViewGood())
4861 m_pwndRight->BuildMarkedWordArray();
4862 if (IsBottomViewGood())
4863 m_pwndBottom->BuildMarkedWordArray();
4864 UpdateLocator();
4865 RecalcAllVertScrollBars();
4866 RecalcAllHorzScrollBars();
4869 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
4871 RebuildIfNecessary();
4873 int nScreenLineCount = (int)m_Screen2View.size();
4875 int nPos = 0;
4876 if (nScreenLineCount>16)
4878 // for enough long data search for last screen
4879 // with viewline less than one we are looking for
4880 // use approximate method (based on) binary search using asymmetric start point
4881 // in form 2**n (determined as MSB of length) to go around division and rounding;
4882 // this effectively looks for bit values from MSB to LSB
4884 int nTestBit;
4885 //GetMostSignificantBitValue
4886 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
4887 nTestBit = nScreenLineCount;
4888 nTestBit |= nTestBit>>1;
4889 nTestBit |= nTestBit>>2;
4890 nTestBit |= nTestBit>>4;
4891 nTestBit |= nTestBit>>8;
4892 nTestBit |= nTestBit>>16;
4893 nTestBit ^= (nTestBit>>1);
4895 while (nTestBit)
4897 int nTestPos = nPos | nTestBit;
4898 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
4900 nPos = nTestPos;
4902 nTestBit >>= 1;
4905 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
4907 nPos++;
4910 return nPos;
4913 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
4914 m_bFull = true;
4916 m_pViewData = pViewData;
4919 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
4921 if (m_bFull)
4922 return;
4924 m_pViewData = pViewData;
4926 TRebuildRange Range;
4927 Range.FirstViewLine=nFirstViewLine;
4928 Range.LastViewLine=nLastViewLine;
4929 m_RebuildRanges.push_back(Range);
4932 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
4934 if (!IsViewGood(pwndView))
4936 return false;
4938 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
4939 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
4940 if (nOldSize == nViewCount)
4942 return false;
4944 pwndView->m_ScreenedViewLine.resize(nViewCount);
4945 return true;
4948 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView)
4950 if (!IsViewGood(pwndView))
4952 return false;
4954 TRebuildRange Range={0, pwndView->GetViewCount()-1};
4955 ResetScreenedViewLineCache(pwndView, Range);
4956 return true;
4959 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range)
4961 if (!IsViewGood(pwndView))
4963 return false;
4965 if (Range.LastViewLine == -1)
4967 return false;
4969 ASSERT(Range.FirstViewLine >= 0);
4970 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
4971 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
4973 pwndView->m_ScreenedViewLine[i].Clear();
4975 return false;
4978 void CBaseView::WrapChanged()
4980 m_nMaxLineLength = -1;
4981 m_nOffsetChar = 0;
4984 void CBaseView::OnEditFind()
4986 if (m_pFindDialog)
4987 return;
4989 m_pFindDialog = new CFindDlg(this);
4990 m_pFindDialog->Create(this);
4992 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
4995 LRESULT CBaseView::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
4997 ASSERT(m_pFindDialog != NULL);
4999 if (m_pFindDialog->IsTerminating())
5001 // invalidate the handle identifying the dialog box.
5002 m_pFindDialog = NULL;
5003 return 0;
5006 if(m_pFindDialog->FindNext())
5008 //read data from dialog
5009 m_sFindText = m_pFindDialog->GetFindString();
5010 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5011 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5012 m_bWholeWord = m_pFindDialog->WholeWord();
5014 if (!m_bMatchCase)
5015 m_sFindText = m_sFindText.MakeLower();
5017 BuildFindStringArray();
5018 OnEditFindnext();
5021 return 0;
5024 void CBaseView::OnEditFindnextStart()
5026 if (m_pViewData == nullptr)
5027 return;
5028 if (HasTextSelection())
5030 m_sFindText = GetSelectedText();
5031 m_bMatchCase = false;
5032 m_bLimitToDiff = false;
5033 m_bWholeWord = false;
5034 m_sFindText = m_sFindText.MakeLower();
5036 BuildFindStringArray();
5037 OnEditFindnext();
5039 else
5041 m_sFindText.Empty();
5042 BuildFindStringArray();
5046 void CBaseView::OnEditFindprevStart()
5048 if (m_pViewData == nullptr)
5049 return;
5050 if (HasTextSelection())
5052 m_sFindText = GetSelectedText();
5053 m_bMatchCase = false;
5054 m_bLimitToDiff = false;
5055 m_bWholeWord = false;
5056 m_sFindText = m_sFindText.MakeLower();
5058 BuildFindStringArray();
5059 OnEditFindprev();
5061 else
5063 m_sFindText.Empty();
5064 BuildFindStringArray();
5068 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5070 start = str.Find(m_sFindText);
5071 if ((srchDir==SearchPrevious)&&(start>=0))
5073 int laststart = start;
5076 start = laststart;
5077 laststart = str.Find(m_sFindText, laststart+1);
5078 } while (laststart >= 0);
5080 end = start + m_sFindText.GetLength();
5081 bool bStringFound = (start >= 0);
5082 if (bStringFound && m_bWholeWord)
5084 if (start)
5085 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5087 if (bStringFound)
5089 if (str.GetLength() > end)
5090 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5093 return bStringFound;
5096 void CBaseView::OnEditFindprev()
5098 Search(SearchPrevious);
5101 void CBaseView::OnEditFindnext()
5103 Search(SearchNext);
5106 void CBaseView::Search(SearchDirection srchDir)
5108 if (m_sFindText.IsEmpty())
5109 return;
5110 if(!m_pViewData)
5111 return;
5113 POINT start = m_ptSelectionViewPosEnd;
5114 POINT end;
5115 end.y = m_pViewData->GetCount()-1;
5116 if (end.y < 0)
5117 return;
5119 if (srchDir==SearchNext)
5120 end.x = GetViewLineLength(end.y);
5121 else
5123 end.x = m_ptSelectionViewPosStart.x;
5124 start.x = 0;
5127 if (!HasTextSelection())
5129 start.y = m_ptCaretViewPos.y;
5130 if (srchDir==SearchNext)
5131 start.x = m_ptCaretViewPos.x;
5132 else
5134 start.x = 0;
5135 end.x = m_ptCaretViewPos.x;
5138 CString sSelectedText;
5139 int startline = -1;
5140 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5142 if (nViewLine < 0)
5144 nViewLine = m_pViewData->GetCount()-1;
5145 startline = start.y;
5147 if (nViewLine > end.y)
5149 nViewLine = 0;
5150 startline = start.y;
5152 if (startline >= 0)
5154 if (nViewLine == startline)
5155 break;
5157 switch (m_pViewData->GetState(nViewLine))
5159 case DIFFSTATE_EMPTY:
5160 break;
5161 case DIFFSTATE_UNKNOWN:
5162 case DIFFSTATE_NORMAL:
5163 if (m_bLimitToDiff)
5164 break;
5165 case DIFFSTATE_REMOVED:
5166 case DIFFSTATE_REMOVEDWHITESPACE:
5167 case DIFFSTATE_ADDED:
5168 case DIFFSTATE_ADDEDWHITESPACE:
5169 case DIFFSTATE_WHITESPACE:
5170 case DIFFSTATE_WHITESPACE_DIFF:
5171 case DIFFSTATE_CONFLICTED:
5172 case DIFFSTATE_CONFLICTED_IGNORED:
5173 case DIFFSTATE_CONFLICTADDED:
5174 case DIFFSTATE_CONFLICTEMPTY:
5175 case DIFFSTATE_CONFLICTRESOLVED:
5176 case DIFFSTATE_IDENTICALREMOVED:
5177 case DIFFSTATE_IDENTICALADDED:
5178 case DIFFSTATE_THEIRSREMOVED:
5179 case DIFFSTATE_THEIRSADDED:
5180 case DIFFSTATE_MOVED_FROM:
5181 case DIFFSTATE_MOVED_TO:
5182 case DIFFSTATE_YOURSREMOVED:
5183 case DIFFSTATE_YOURSADDED:
5184 case DIFFSTATE_EDITED:
5186 sSelectedText = GetViewLineChars(nViewLine);
5187 if (nViewLine==start.y)
5188 sSelectedText = srchDir==SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(start.x);
5189 if (!m_bMatchCase)
5190 sSelectedText = sSelectedText.MakeLower();
5191 int startfound = -1;
5192 int endfound = -1;
5193 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5195 HighlightViewLines(nViewLine, nViewLine);
5196 m_ptSelectionViewPosStart.x = startfound;
5197 m_ptSelectionViewPosEnd.x = endfound;
5198 if (nViewLine==start.y)
5200 m_ptSelectionViewPosStart.x += start.x;
5201 m_ptSelectionViewPosEnd.x += start.x;
5203 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5204 m_ptSelectionViewPosStart.y = nViewLine;
5205 m_ptSelectionViewPosEnd.y = nViewLine;
5206 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5207 UpdateViewsCaretPosition();
5208 EnsureCaretVisible();
5209 Invalidate();
5210 return;
5213 break;
5216 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5219 CString CBaseView::GetSelectedText() const
5221 CString sSelectedText;
5222 POINT start = m_ptSelectionViewPosStart;
5223 POINT end = m_ptSelectionViewPosEnd;
5224 if (!HasTextSelection())
5226 if (!HasSelection())
5227 return sSelectedText;
5228 start.y = m_nSelViewBlockStart;
5229 start.x = 0;
5230 end.y = m_nSelViewBlockEnd;
5231 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5233 // first store the selected lines in one CString
5234 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5236 switch (m_pViewData->GetState(nViewLine))
5238 case DIFFSTATE_EMPTY:
5239 break;
5240 case DIFFSTATE_UNKNOWN:
5241 case DIFFSTATE_NORMAL:
5242 case DIFFSTATE_REMOVED:
5243 case DIFFSTATE_REMOVEDWHITESPACE:
5244 case DIFFSTATE_ADDED:
5245 case DIFFSTATE_ADDEDWHITESPACE:
5246 case DIFFSTATE_WHITESPACE:
5247 case DIFFSTATE_WHITESPACE_DIFF:
5248 case DIFFSTATE_CONFLICTED:
5249 case DIFFSTATE_CONFLICTED_IGNORED:
5250 case DIFFSTATE_CONFLICTADDED:
5251 case DIFFSTATE_CONFLICTEMPTY:
5252 case DIFFSTATE_CONFLICTRESOLVED:
5253 case DIFFSTATE_IDENTICALREMOVED:
5254 case DIFFSTATE_IDENTICALADDED:
5255 case DIFFSTATE_THEIRSREMOVED:
5256 case DIFFSTATE_THEIRSADDED:
5257 case DIFFSTATE_MOVED_FROM:
5258 case DIFFSTATE_MOVED_TO:
5259 case DIFFSTATE_YOURSREMOVED:
5260 case DIFFSTATE_YOURSADDED:
5261 case DIFFSTATE_EDITED:
5262 sSelectedText += GetViewLineChars(nViewLine);
5263 sSelectedText += _T("\r\n");
5264 break;
5267 // remove the non-selected chars from the first line, last line and last \r\n
5268 int nLeftCut = start.x;
5269 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5270 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5271 return sSelectedText;
5274 void CBaseView::OnEditGotoline()
5276 if (m_pViewData == NULL)
5277 return;
5278 // find the last and first line number
5279 int nViewLineCount = m_pViewData->GetCount();
5281 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5282 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5284 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5285 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5287 break;
5290 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5292 return;
5294 nLastLineNumber++;
5295 int nFirstLineNumber=1; // first is always 1
5297 CString sText;
5298 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5300 CGotoLineDlg dlg(this);
5301 dlg.SetLabel(sText);
5302 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5303 if (dlg.DoModal() == IDOK)
5305 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5307 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5309 HighlightViewLines(nViewLine, nViewLine);
5310 return;
5316 void CBaseView::OnToggleReadonly()
5318 if (IsReadonlyChangable()) {
5319 SetWritable(IsReadonly());
5323 int CBaseView::SaveFile()
5325 Invalidate();
5326 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5328 CFileTextLines file;
5329 //file.SetSaveParams(m_SaveParams);
5331 for (int i=0; i<m_pViewData->GetCount(); i++)
5333 //only copy non-removed lines
5334 DiffStates state = m_pViewData->GetState(i);
5335 switch (state)
5337 case DIFFSTATE_CONFLICTED:
5338 case DIFFSTATE_CONFLICTED_IGNORED:
5340 int first = i;
5341 int last = i;
5344 last++;
5345 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5346 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5347 for (int j=first; j<last; j++)
5349 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5351 file.Add(_T("======="), EOL_NOENDING);
5352 for (int j=first; j<last; j++)
5354 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5356 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5357 i = last-1;
5359 break;
5360 case DIFFSTATE_EMPTY:
5361 case DIFFSTATE_CONFLICTEMPTY:
5362 case DIFFSTATE_IDENTICALREMOVED:
5363 case DIFFSTATE_REMOVED:
5364 case DIFFSTATE_THEIRSREMOVED:
5365 case DIFFSTATE_YOURSREMOVED:
5366 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5367 // do not save removed lines
5368 break;
5369 default:
5370 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5371 break;
5374 if (!file.Save(m_pWorkingFile->GetFilename()))
5376 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseMerge"), MB_ICONERROR);
5377 return -1;
5379 m_pWorkingFile->StoreFileAttributes();
5380 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5381 SetModified(FALSE);
5382 CUndo::GetInstance().MarkAsOriginalState();
5383 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5384 return 0;
5385 return file.GetCount();
5387 return 1;
5391 int CBaseView::SaveFileTo(CString sFileName)
5393 if (m_pWorkingFile)
5395 m_pWorkingFile->SetFileName(sFileName);
5396 return SaveFile();
5398 return -1;