TortoiseGitMerge: Press Ctrl+M should not insert newline
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob267b66f8872fa58e4d121a95fc6273ac4eb45636
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2019 - TortoiseSVN
4 // Copyright (C) 2011-2012, 2017-2019 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"
30 #include "EncodingDlg.h"
31 #include "EditorConfigWrapper.h"
32 #include "DPIAware.h"
34 // Note about lines:
35 // We use three different kind of lines here:
36 // 1. The real lines of the original files.
37 // These are shown in the view margin and are not used elsewhere, they're only for user information.
38 // 2. Screen lines.
39 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
40 // 3. View lines.
41 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
43 // Basically view lines are the line data, while screen lines are shown lines.
46 #ifdef _DEBUG
47 #define new DEBUG_NEW
48 #endif
50 #define HEADERHEIGHT 10
52 #define IDT_SCROLLTIMER 101
54 CBaseView* CBaseView::m_pwndLeft = nullptr;
55 CBaseView* CBaseView::m_pwndRight = nullptr;
56 CBaseView* CBaseView::m_pwndBottom = nullptr;
57 CLocatorBar* CBaseView::m_pwndLocator = nullptr;
58 CLineDiffBar* CBaseView::m_pwndLineDiffBar = nullptr;
59 CMFCStatusBar* CBaseView::m_pwndStatusBar = nullptr;
60 CMFCRibbonStatusBar* CBaseView::m_pwndRibbonStatusBar = nullptr;
61 CMainFrame* CBaseView::m_pMainFrame = nullptr;
62 CBaseView::Screen2View CBaseView::m_Screen2View;
63 const UINT CBaseView::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
65 allviewstate CBaseView::m_AllState;
67 IMPLEMENT_DYNCREATE(CBaseView, CView)
69 CBaseView::CBaseView()
70 : m_pCacheBitmap(nullptr)
71 , m_pViewData(nullptr)
72 , m_pOtherViewData(nullptr)
73 , m_pOtherView(nullptr)
74 , m_nLineHeight(-1)
75 , m_nCharWidth(-1)
76 , m_nScreenChars(-1)
77 , m_nLastScreenChars(-1)
78 , m_nMaxLineLength(-1)
79 , m_nScreenLines(-1)
80 , m_nTopLine(0)
81 , m_nOffsetChar(0)
82 , m_nDigits(0)
83 , m_nMouseLine(-1)
84 , m_mouseInMargin(false)
85 , m_bIsHidden(FALSE)
86 , m_lineendings(EOL_AUTOLINE)
87 , m_bReadonly(true)
88 , m_bReadonlyIsChangable(false)
89 , m_bTarget(false)
90 , m_nCaretGoalPos(0)
91 , m_nSelViewBlockStart(-1)
92 , m_nSelViewBlockEnd(-1)
93 , m_bFocused(FALSE)
94 , m_bShowSelection(true)
95 , m_texttype(CFileTextLines::AUTOTYPE)
96 , m_bModified(FALSE)
97 , m_bOtherDiffChecked(false)
98 , m_bInlineWordDiff(true)
99 , m_bWhitespaceInlineDiffs(false)
100 , m_pState(nullptr)
101 , m_pFindDialog(nullptr)
102 , m_nStatusBarID(0)
103 , m_bMatchCase(false)
104 , m_bLimitToDiff(true)
105 , m_bWholeWord(false)
106 , m_pDC(nullptr)
107 , m_pWorkingFile(nullptr)
108 , m_bInsertMode(true)
109 , m_bEditorConfigEnabled(false)
110 , m_bEditorConfigLoaded(2) // 2 = not evaluated
112 m_ptCaretViewPos.x = 0;
113 m_ptCaretViewPos.y = 0;
114 m_ptSelectionViewPosStart = m_ptCaretViewPos;
115 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
116 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
117 m_bViewWhitespace = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
118 m_bViewLinenumbers = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
119 m_bShowInlineDiff = CRegDWORD(L"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE);
120 m_nInlineDiffMaxLineLength = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
121 m_InlineAddedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR);
122 m_InlineRemovedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR);
123 m_ModifiedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR);
124 m_WhiteSpaceFg = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW));
125 m_sWordSeparators = CRegString(L"Software\\TortoiseGitMerge\\WordSeparators", L"[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?");
126 m_bIconLFs = CRegDWORD(L"Software\\TortoiseGitMerge\\IconLFs", 0);
127 m_nTabSize = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4));
128 m_nTabMode = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE));
129 m_bEditorConfigEnabled = !!static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE));
130 std::fill_n(m_apFonts, fontsCount, static_cast<CFont*>(nullptr));
132 int cxIcon = GetSystemMetrics(SM_CXSMICON);
133 int cyIcon = GetSystemMetrics(SM_CYSMICON);
134 m_hConflictedIcon = CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDLINE, cxIcon, cyIcon);
135 m_hConflictedIgnoredIcon = CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDIGNOREDLINE, cxIcon, cyIcon);
136 m_hRemovedIcon = CCommonAppUtils::LoadIconEx(IDI_REMOVEDLINE, cxIcon, cyIcon);
137 m_hAddedIcon = CCommonAppUtils::LoadIconEx(IDI_ADDEDLINE, cxIcon, cyIcon);
138 m_hWhitespaceBlockIcon = CCommonAppUtils::LoadIconEx(IDI_WHITESPACELINE, cxIcon, cyIcon);
139 m_hEqualIcon = CCommonAppUtils::LoadIconEx(IDI_EQUALLINE, cxIcon, cyIcon);
140 m_hLineEndingCR = CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCR, cxIcon, cyIcon);
141 m_hLineEndingCRLF = CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCRLF, cxIcon, cyIcon);
142 m_hLineEndingLF = CCommonAppUtils::LoadIconEx(IDI_LINEENDINGLF, cxIcon, cyIcon);
143 m_hEditedIcon = CCommonAppUtils::LoadIconEx(IDI_LINEEDITED, cxIcon, cyIcon);
144 m_hMovedIcon = CCommonAppUtils::LoadIconEx(IDI_MOVEDLINE, cxIcon, cyIcon);
145 m_hMarkedIcon = CCommonAppUtils::LoadIconEx(IDI_LINEMARKED, cxIcon, cyIcon);
146 m_margincursor = static_cast<HCURSOR>(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE));
148 for (int i=0; i<1024; ++i)
149 m_sConflictedText += L"??";
150 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
152 m_szTip[0] = 0;
153 m_wszTip[0] = 0;
154 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
155 EnableToolTips();
157 m_Eols[EOL_LF] = L"\n"; // x0a
158 m_Eols[EOL_CR] = L"\r"; // x0d
159 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
160 m_Eols[EOL_LFCR] = L"\n\r";
161 m_Eols[EOL_VT] = L"\v"; // x0b
162 m_Eols[EOL_FF] = L"\f"; // x0c
163 m_Eols[EOL_NEL] = L"\x85";
164 m_Eols[EOL_LS] = L"\x2028";
165 m_Eols[EOL_PS] = L"\x2029";
166 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
167 ? EOL_CRLF
168 : m_lineendings];
169 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
170 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
173 CBaseView::~CBaseView()
175 ReleaseBitmap();
176 DeleteFonts();
177 DestroyIcon(m_hAddedIcon);
178 DestroyIcon(m_hRemovedIcon);
179 DestroyIcon(m_hConflictedIcon);
180 DestroyIcon(m_hConflictedIgnoredIcon);
181 DestroyIcon(m_hWhitespaceBlockIcon);
182 DestroyIcon(m_hEqualIcon);
183 DestroyIcon(m_hLineEndingCR);
184 DestroyIcon(m_hLineEndingCRLF);
185 DestroyIcon(m_hLineEndingLF);
186 DestroyIcon(m_hEditedIcon);
187 DestroyIcon(m_hMovedIcon);
188 DestroyIcon(m_hMarkedIcon);
189 DestroyCursor(m_margincursor);
192 BEGIN_MESSAGE_MAP(CBaseView, CView)
193 ON_WM_VSCROLL()
194 ON_WM_HSCROLL()
195 ON_WM_ERASEBKGND()
196 ON_WM_CREATE()
197 ON_WM_DESTROY()
198 ON_WM_SIZE()
199 ON_WM_MOUSEWHEEL()
200 ON_WM_MOUSEHWHEEL()
201 ON_WM_SETCURSOR()
202 ON_WM_KILLFOCUS()
203 ON_WM_SETFOCUS()
204 ON_WM_CONTEXTMENU()
205 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
206 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
207 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
208 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
209 ON_WM_KEYDOWN()
210 ON_WM_LBUTTONDOWN()
211 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
212 ON_WM_MOUSEMOVE()
213 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
214 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
215 ON_WM_CHAR()
216 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
217 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
218 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
219 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
220 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
221 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
222 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
223 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
224 ON_WM_TIMER()
225 ON_WM_LBUTTONDBLCLK()
226 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
227 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
228 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
229 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
230 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
231 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
232 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
233 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
234 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
235 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
236 ON_WM_LBUTTONUP()
237 END_MESSAGE_MAP()
240 void CBaseView::DocumentUpdated()
242 ReleaseBitmap();
243 m_nLineHeight = -1;
244 m_nCharWidth = -1;
245 m_nScreenChars = -1;
246 m_nLastScreenChars = -1;
247 m_nMaxLineLength = -1;
248 m_nScreenLines = -1;
249 m_nTopLine = 0;
250 m_bModified = FALSE;
251 m_bOtherDiffChecked = false;
252 m_nDigits = 0;
253 m_nMouseLine = -1;
254 m_nTabSize = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4));
255 m_nTabMode = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE));
256 m_bViewLinenumbers = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
257 m_InlineAddedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR);
258 m_InlineRemovedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR);
259 m_ModifiedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR);
260 m_WhiteSpaceFg = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW));
261 m_bIconLFs = CRegDWORD(L"Software\\TortoiseGitMerge\\IconLFs", 0);
262 m_nInlineDiffMaxLineLength = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
263 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
264 ? EOL_CRLF
265 : m_lineendings];
266 SetEditorConfigEnabled(m_bEditorConfigEnabled);
267 DeleteFonts();
268 ClearCurrentSelection();
269 UpdateStatusBar();
270 Invalidate();
273 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled)
275 m_bEditorConfigEnabled = bEditorConfigEnabled;
276 m_nTabSize = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4));
277 m_nTabMode = static_cast<int>(CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE));
278 if (m_bEditorConfigEnabled)
280 m_bEditorConfigLoaded = FALSE; // no editorconfig entries loaded
281 CEditorConfigWrapper ec;
282 if (ec.Load(m_sReflectedName.IsEmpty() ? m_sFullFilePath : m_sReflectedName))
284 m_bEditorConfigLoaded = TRUE;
285 if (ec.m_nTabWidth != nullptr)
286 m_nTabSize = ec.m_nTabWidth;
287 if (ec.m_bIndentStyle != nullptr)
288 m_nTabMode = (m_nTabMode & ~TABMODE_USESPACES) | (ec.m_bIndentStyle ? TABMODE_USESPACES : TABMODE_NONE);
293 static CString GetTabModeString(int nTabMode, int nTabSize, bool bEditorConfig)
295 CString text;
296 if (nTabMode & TABMODE_USESPACES)
297 text = L"Space";
298 else
299 text = L"Tab";
300 text.AppendFormat(L" %d", nTabSize);
301 if (nTabMode & TABMODE_SMARTINDENT)
302 text += L" Smart";
303 if (bEditorConfig)
304 text += L" EC";
305 return text;
308 void CBaseView::UpdateStatusBar()
310 int nRemovedLines = 0;
311 int nAddedLines = 0;
312 int nConflictedLines = 0;
314 if (m_pViewData)
316 for (int i=0; i<m_pViewData->GetCount(); i++)
318 DiffStates state = m_pViewData->GetState(i);
319 switch (state)
321 case DIFFSTATE_ADDED:
322 case DIFFSTATE_IDENTICALADDED:
323 case DIFFSTATE_THEIRSADDED:
324 case DIFFSTATE_YOURSADDED:
325 case DIFFSTATE_CONFLICTADDED:
326 nAddedLines++;
327 break;
328 case DIFFSTATE_IDENTICALREMOVED:
329 case DIFFSTATE_REMOVED:
330 case DIFFSTATE_THEIRSREMOVED:
331 case DIFFSTATE_YOURSREMOVED:
332 nRemovedLines++;
333 break;
334 case DIFFSTATE_CONFLICTED:
335 case DIFFSTATE_CONFLICTED_IGNORED:
336 nConflictedLines++;
337 break;
342 CString sBarText;
343 CString sTemp;
345 if (m_pwndStatusBar)
347 sBarText += CFileTextLines::GetEncodingName(m_texttype);
348 sBarText += sBarText.IsEmpty() ? L"" : L" ";
349 sBarText += GetEolName(m_lineendings);
350 sBarText += sBarText.IsEmpty() ? L"" : L" ";
352 if (sBarText.IsEmpty())
353 sBarText += L" / ";
356 if (nRemovedLines)
358 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
359 if (!sBarText.IsEmpty())
360 sBarText += L" / ";
361 sBarText += sTemp;
363 if (nAddedLines)
365 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
366 if (!sBarText.IsEmpty())
367 sBarText += L" / ";
368 sBarText += sTemp;
370 if (nConflictedLines)
372 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
373 if (!sBarText.IsEmpty())
374 sBarText += L" / ";
375 sBarText += sTemp;
377 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
379 if (m_pwndStatusBar)
381 UINT nID;
382 UINT nStyle;
383 int cxWidth;
384 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
386 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
388 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
390 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
391 sBarText = sTemp+sBarText;
393 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
395 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
396 sBarText = sTemp+sBarText;
398 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
399 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
400 //calculate the width of the text
401 CDC * pDC = m_pwndStatusBar->GetDC();
402 if (pDC)
404 CSize size = pDC->GetTextExtent(sBarText);
405 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
406 ReleaseDC(pDC);
408 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
410 else if (m_pwndRibbonStatusBar)
412 if (!IsViewGood(m_pwndBottom))
413 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
414 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
416 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
417 auto apBtnGroupBottom = std::make_unique<CMFCRibbonButtonsGroup>();
418 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
419 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
420 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
421 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
422 apBtnGroupBottom->AddButton(pButton);
423 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
424 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
425 apBtnGroupBottom->AddButton(pButton);
426 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, L"");
427 m_pMainFrame->FillTabModeButton(pButton, ID_INDICATOR_BOTTOMTABMODESTART);
428 apBtnGroupBottom->AddButton(pButton);
429 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
430 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
433 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
434 if (pGroup)
436 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(4));
437 if (pPane)
439 pPane->SetText(sBarText);
441 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
442 if (pButton)
444 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
445 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
447 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
448 if (pButton)
450 pButton->SetText(GetEolName(m_lineendings));
451 pButton->SetDescription(GetEolName(m_lineendings));
453 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(3));
454 if (pButton)
456 pButton->SetText(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
457 pButton->SetDescription(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
460 m_pwndRibbonStatusBar->RecalcLayout();
461 m_pwndRibbonStatusBar->Invalidate();
466 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
468 if (!CView::PreCreateWindow(cs))
469 return FALSE;
471 cs.dwExStyle |= WS_EX_CLIENTEDGE;
472 cs.style &= ~WS_BORDER;
473 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
474 ::LoadCursor(nullptr, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1), nullptr);
476 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
477 if (!pParentWnd || !pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
479 // View must always create its own scrollbars,
480 // if only it's not used within splitter
481 cs.style |= (WS_HSCROLL | WS_VSCROLL);
483 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
484 return TRUE;
487 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
489 int nIndex = 0;
490 if (bBold)
491 nIndex |= 1;
492 if (bItalic)
493 nIndex |= 2;
494 if (!m_apFonts[nIndex])
496 m_apFonts[nIndex] = new CFont;
497 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
498 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
499 m_lfBaseFont.lfItalic = static_cast<BYTE>(bItalic);
500 m_lfBaseFont.lfHeight = -CDPIAware::Instance().PointsToPixelsY(static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGitMerge\\LogFontSize", 10)));
501 wcsncpy_s(m_lfBaseFont.lfFaceName, static_cast<LPCTSTR>(static_cast<CString>(CRegString(L"Software\\TortoiseGitMerge\\LogFontName", L"Consolas"))), _countof(m_lfBaseFont.lfFaceName) - 1);
502 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
504 delete m_apFonts[nIndex];
505 m_apFonts[nIndex] = nullptr;
506 return CView::GetFont();
509 return m_apFonts[nIndex];
512 void CBaseView::CalcLineCharDim()
514 CDC *pDC = GetDC();
515 if (!pDC)
516 return;
517 CFont *pOldFont = pDC->SelectObject(GetFont());
518 const CSize szCharExt = pDC->GetTextExtent(L"X");
519 pDC->SelectObject(pOldFont);
520 ReleaseDC(pDC);
522 m_nLineHeight = szCharExt.cy;
523 if (m_nLineHeight <= 0)
524 m_nLineHeight = -1;
525 m_nCharWidth = szCharExt.cx;
526 if (m_nCharWidth <= 0)
527 m_nCharWidth = -1;
530 int CBaseView::GetScreenChars()
532 if (m_nScreenChars == -1)
534 CRect rect;
535 GetClientRect(&rect);
536 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
537 if (m_nScreenChars < 0)
538 m_nScreenChars = 0;
540 return m_nScreenChars;
543 int CBaseView::GetAllMinScreenChars() const
545 int nChars = INT_MAX;
546 if (IsLeftViewGood())
547 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
548 if (IsRightViewGood())
549 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
550 if (IsBottomViewGood())
551 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
552 return (nChars==INT_MAX) ? 0 : nChars;
555 int CBaseView::GetAllMaxLineLength() const
557 int nLength = 0;
558 if (IsLeftViewGood())
559 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
560 if (IsRightViewGood())
561 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
562 if (IsBottomViewGood())
563 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
564 return nLength;
567 int CBaseView::GetLineHeight()
569 if (m_nLineHeight == -1)
570 CalcLineCharDim();
571 if (m_nLineHeight <= 0)
572 return 1;
573 return m_nLineHeight;
576 int CBaseView::GetCharWidth()
578 if (m_nCharWidth == -1)
579 CalcLineCharDim();
580 if (m_nCharWidth <= 0)
581 return 1;
582 return m_nCharWidth;
585 int CBaseView::GetMaxLineLength()
587 if (m_nMaxLineLength == -1)
589 m_nMaxLineLength = 0;
590 int nLineCount = GetLineCount();
591 if (nLineCount == 1)
593 m_nMaxLineLength = GetLineLengthWithTabsConverted(0);
594 return m_nMaxLineLength;
596 for (int i=0; i<nLineCount; i++)
598 int nActualLength = GetLineLengthWithTabsConverted(i);
599 if (m_nMaxLineLength < nActualLength)
600 m_nMaxLineLength = nActualLength;
603 return m_nMaxLineLength;
606 int CBaseView::GetLineLengthWithTabsConverted(int index)
608 if (!m_pViewData)
609 return 0;
610 if (m_pViewData->GetCount() == 0)
611 return 0;
612 if (m_Screen2View.size() <= index)
613 return 0;
614 CString sLine;
615 if (m_pMainFrame->m_bWrapLines)
616 sLine = GetLineChars(index);
617 else
619 int viewLine = GetViewLineForScreen(index);
620 sLine = m_pViewData->GetLine(viewLine);
622 int tabCount = 0;
623 const wchar_t* pChar = sLine;
624 auto nLineLength = sLine.GetLength();
625 for (int i = 0; i < nLineLength; ++i)
627 if (*pChar == '\t')
628 ++tabCount;
629 ++pChar;
631 // GetTabSize() - 1 because the tabs are already counted
632 nLineLength = nLineLength + (tabCount * (GetTabSize() - 1));
633 ASSERT(nLineLength >= 0);
634 return nLineLength;
637 int CBaseView::GetLineLength(int index)
639 if (!m_pViewData)
640 return 0;
641 if (m_pViewData->GetCount() == 0)
642 return 0;
643 if (m_Screen2View.size() <= index)
644 return 0;
645 int viewLine = GetViewLineForScreen(index);
646 if (m_pMainFrame->m_bWrapLines)
648 int nLineLength = GetLineChars(index).GetLength();
649 ASSERT(nLineLength >= 0);
650 return nLineLength;
652 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
653 ASSERT(nLineLength >= 0);
654 return nLineLength;
657 int CBaseView::GetViewLineLength(int nViewLine) const
659 if (!m_pViewData)
660 return 0;
661 if (m_pViewData->GetCount() <= nViewLine)
662 return 0;
663 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
664 ASSERT(nLineLength >= 0);
665 return nLineLength;
668 int CBaseView::GetLineCount() const
670 if (!m_pViewData)
671 return 1;
672 int nLineCount = m_Screen2View.size();
673 ASSERT(nLineCount >= 0);
674 return nLineCount;
677 int CBaseView::GetSubLineOffset(int index)
679 return m_Screen2View.GetSubLineOffset(index);
682 CString CBaseView::GetViewLineChars(int nViewLine) const
684 if (!m_pViewData)
685 return 0;
686 if (m_pViewData->GetCount() <= nViewLine)
687 return 0;
688 return m_pViewData->GetLine(nViewLine);
691 CString CBaseView::GetLineChars(int index)
693 if (!m_pViewData)
694 return 0;
695 if (m_pViewData->GetCount() == 0)
696 return 0;
697 if (m_Screen2View.size() <= index)
698 return 0;
699 int viewLine = GetViewLineForScreen(index);
700 if (m_pMainFrame->m_bWrapLines)
702 int subLine = GetSubLineOffset(index);
703 if (subLine >= 0)
705 if (subLine < CountMultiLines(viewLine))
707 return m_ScreenedViewLine[viewLine].SubLines[subLine];
709 return L"";
712 return m_pViewData->GetLine(viewLine);
715 void CBaseView::CheckOtherView()
717 if (m_bOtherDiffChecked)
718 return;
719 // find out what the 'other' file is
720 m_pOtherViewData = nullptr;
721 m_pOtherView = nullptr;
722 if (this == m_pwndLeft && IsRightViewGood())
724 m_pOtherViewData = m_pwndRight->m_pViewData;
725 m_pOtherView = m_pwndRight;
728 if (this == m_pwndRight && IsLeftViewGood())
730 m_pOtherViewData = m_pwndLeft->m_pViewData;
731 m_pOtherView = m_pwndLeft;
734 m_bOtherDiffChecked = true;
738 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
740 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
741 ASSERT(viewData);
743 DiffStates origstate = viewData->GetState(nLineIndex);
745 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
746 nStartBlock = nLineIndex;
747 nEndBlock = nLineIndex;
748 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
750 DiffStates state = viewData->GetState(nStartBlock - 1);
751 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
752 origstate = state;
753 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
754 nStartBlock--;
755 else
756 break;
758 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
760 DiffStates state = viewData->GetState(nEndBlock + 1);
761 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
762 origstate = state;
763 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
764 nEndBlock++;
765 else
766 break;
770 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
772 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
774 int len = 0;
775 for (int i = nStartBlock; i <= nEndBlock; ++i)
776 len += viewData->GetLine(i).GetLength()+2;
778 CString block;
779 // do not check for whitespace blocks if the line is too long, because
780 // reserving a lot of memory here takes too much time (performance hog)
781 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
782 return block;
783 block.Preallocate(len+1);
784 for (int i = nStartBlock; i <= nEndBlock; ++i)
786 block += viewData->GetLine(i);
787 block += m_Eols[viewData->GetLineEnding(i)];
789 return block;
792 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
794 if (!m_pViewData)
795 return false;
796 bIdentical = false;
797 CheckOtherView();
798 if (!m_pOtherViewData)
799 return false;
800 int viewLine = GetViewLineForScreen(nLineIndex);
801 if (
802 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
803 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
806 bIdentical = true;
807 return false;
809 // first check whether the line itself only has whitespace changes
810 CString mine = m_pViewData->GetLine(viewLine);
811 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
812 if (mine.IsEmpty() && other.IsEmpty())
814 bIdentical = true;
815 return false;
818 if (mine == other)
820 bIdentical = true;
821 return true;
823 FilterWhitespaces(mine, other);
824 if (mine == other)
825 return true;
827 int nStartBlock2, nEndBlock2;
828 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
829 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
830 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
831 if (mine.IsEmpty())
832 bIdentical = false;
833 else
835 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
836 bIdentical = mine == other;
837 FilterWhitespaces(mine, other);
840 return (!mine.IsEmpty()) && (mine == other);
843 bool CBaseView::IsViewLineHidden(int nViewLine)
845 return IsViewLineHidden(m_pViewData, nViewLine);
848 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
850 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
853 int CBaseView::GetLineNumber(int index) const
855 if (!m_pViewData)
856 return -1;
857 int viewLine = GetViewLineForScreen(index);
858 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
859 return -1;
860 return m_pViewData->GetLineNumber(viewLine);
863 int CBaseView::GetScreenLines()
865 if (m_nScreenLines == -1)
867 CRect rect;
868 GetClientRect(&rect);
869 SCROLLBARINFO sbi = { sizeof(sbi) };
870 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
872 // only use the scroll bar size if the info is correct and the scrollbar is visible
873 // if anything isn't proper, assume the scrollbar has a size of zero
874 // and calculate the screen lines without it.
875 if (!(sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) && !(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
877 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
878 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
879 if (m_nScreenLines < 0)
880 m_nScreenLines = 0;
881 return m_nScreenLines;
884 // if the scroll bar is not visible, unavailable or there was an error,
885 // assume the scroll bar height is zero and return the screen lines here.
886 // Of course, that means the cache (using the member variable) won't work
887 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
888 return (rect.Height() - HEADERHEIGHT) / GetLineHeight();
890 return m_nScreenLines;
893 int CBaseView::GetAllMinScreenLines() const
895 int nLines = INT_MAX;
896 if (IsLeftViewGood())
897 nLines = m_pwndLeft->GetScreenLines();
898 if (IsRightViewGood())
899 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
900 if (IsBottomViewGood())
901 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
902 return (nLines == INT_MAX) || (nLines < 0) ? 0 : nLines;
905 int CBaseView::GetAllLineCount() const
907 int nLines = 0;
908 if (IsLeftViewGood())
909 nLines = m_pwndLeft->GetLineCount();
910 if (IsRightViewGood())
911 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
912 if (IsBottomViewGood())
913 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
914 return nLines;
917 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
919 if (IsLeftViewGood())
920 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
921 if (IsRightViewGood())
922 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
923 if (IsBottomViewGood())
924 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
927 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
929 SCROLLINFO si;
930 si.cbSize = sizeof(si);
931 if (bPositionOnly)
933 si.fMask = SIF_POS;
934 si.nPos = m_nTopLine;
936 else
938 EnableScrollBarCtrl(SB_VERT, TRUE);
939 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
941 m_nTopLine = 0;
942 Invalidate();
944 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
945 si.nMin = 0;
946 si.nMax = GetAllLineCount();
947 si.nPage = GetAllMinScreenLines();
948 si.nPos = m_nTopLine;
950 VERIFY(SetScrollInfo(SB_VERT, &si));
953 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
955 CView::OnVScroll(nSBCode, nPos, pScrollBar);
956 if (m_pwndLeft)
957 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
958 if (m_pwndRight)
959 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
960 if (m_pwndBottom)
961 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
962 if (m_pwndLocator)
963 m_pwndLocator->Invalidate();
966 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
968 // Note we cannot use nPos because of its 16-bit nature
969 SCROLLINFO si;
970 si.cbSize = sizeof(si);
971 si.fMask = SIF_ALL;
972 VERIFY(master->GetScrollInfo(SB_VERT, &si));
974 int nPageLines = GetScreenLines();
975 int nLineCount = GetLineCount();
977 int nNewTopLine;
979 static LONG textwidth = 0;
980 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
981 switch (nSBCode)
983 case SB_TOP:
984 nNewTopLine = 0;
985 break;
986 case SB_BOTTOM:
987 nNewTopLine = nLineCount - nPageLines + 1;
988 break;
989 case SB_LINEUP:
990 nNewTopLine = m_nTopLine - 1;
991 break;
992 case SB_LINEDOWN:
993 nNewTopLine = m_nTopLine + 1;
994 break;
995 case SB_PAGEUP:
996 nNewTopLine = m_nTopLine - si.nPage + 1;
997 break;
998 case SB_PAGEDOWN:
999 nNewTopLine = m_nTopLine + si.nPage - 1;
1000 break;
1001 case SB_THUMBPOSITION:
1002 m_ScrollTool.Clear();
1003 nNewTopLine = si.nTrackPos;
1004 textwidth = 0;
1005 break;
1006 case SB_THUMBTRACK:
1007 nNewTopLine = si.nTrackPos;
1008 if (GetFocus() == this)
1010 RECT thumbrect;
1011 GetClientRect(&thumbrect);
1012 ClientToScreen(&thumbrect);
1014 POINT thumbpoint;
1015 thumbpoint.x = thumbrect.right;
1016 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
1017 m_ScrollTool.Init(&thumbpoint);
1018 if (textwidth == 0)
1020 CString sTemp = sFormat;
1021 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
1022 textwidth = m_ScrollTool.GetTextWidth(sTemp);
1024 thumbpoint.x -= textwidth;
1025 int line = GetLineNumber(nNewTopLine);
1026 if (line >= 0)
1027 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
1028 else
1029 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
1031 break;
1032 default:
1033 return;
1036 if (nNewTopLine < 0)
1037 nNewTopLine = 0;
1038 if (nNewTopLine >= nLineCount)
1039 nNewTopLine = nLineCount - 1;
1040 ScrollToLine(nNewTopLine);
1043 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
1045 if (IsLeftViewGood())
1046 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
1047 if (IsRightViewGood())
1048 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
1049 if (IsBottomViewGood())
1050 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
1053 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
1055 SCROLLINFO si;
1056 si.cbSize = sizeof(si);
1057 if (bPositionOnly)
1059 si.fMask = SIF_POS;
1060 si.nPos = m_nOffsetChar;
1062 else
1064 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
1065 if (!m_pMainFrame->m_bWrapLines)
1067 int minScreenChars = GetAllMinScreenChars();
1068 int maxLineLength = GetAllMaxLineLength();
1069 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1071 m_nOffsetChar = 0;
1072 Invalidate();
1074 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1075 si.nMin = 0;
1076 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1077 si.nMax += GetMarginWidth()/GetCharWidth();
1078 si.nPage = GetScreenChars();
1079 si.nPos = m_nOffsetChar;
1082 VERIFY(SetScrollInfo(SB_HORZ, &si));
1085 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1087 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1088 if (m_pwndLeft)
1089 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1090 if (m_pwndRight)
1091 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1092 if (m_pwndBottom)
1093 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1094 if (m_pwndLocator)
1095 m_pwndLocator->Invalidate();
1098 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1100 SCROLLINFO si;
1101 si.cbSize = sizeof(si);
1102 si.fMask = SIF_ALL;
1103 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1105 int nPageChars = GetScreenChars();
1106 int nMaxLineLength = GetMaxLineLength();
1108 int nNewOffset;
1109 switch (nSBCode)
1111 case SB_LEFT:
1112 nNewOffset = 0;
1113 break;
1114 case SB_BOTTOM:
1115 nNewOffset = nMaxLineLength - nPageChars + 1;
1116 break;
1117 case SB_LINEUP:
1118 nNewOffset = m_nOffsetChar - 1;
1119 break;
1120 case SB_LINEDOWN:
1121 nNewOffset = m_nOffsetChar + 1;
1122 break;
1123 case SB_PAGEUP:
1124 nNewOffset = m_nOffsetChar - si.nPage + 1;
1125 break;
1126 case SB_PAGEDOWN:
1127 nNewOffset = m_nOffsetChar + si.nPage - 1;
1128 break;
1129 case SB_THUMBPOSITION:
1130 case SB_THUMBTRACK:
1131 nNewOffset = si.nTrackPos;
1132 break;
1133 default:
1134 return;
1137 if (nNewOffset >= nMaxLineLength)
1138 nNewOffset = nMaxLineLength - 1;
1139 if (nNewOffset < 0)
1140 nNewOffset = 0;
1141 ScrollToChar(nNewOffset, TRUE);
1144 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1146 if (m_nOffsetChar != nNewOffsetChar)
1148 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1149 m_nOffsetChar = nNewOffsetChar;
1150 CRect rcScroll;
1151 GetClientRect(&rcScroll);
1152 rcScroll.left += GetMarginWidth();
1153 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1154 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1155 // update the view header
1156 rcScroll.left = 0;
1157 rcScroll.top = 0;
1158 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1159 InvalidateRect(&rcScroll, FALSE);
1160 UpdateWindow();
1161 if (bTrackScrollBar)
1162 RecalcHorzScrollBar(TRUE);
1163 UpdateCaret();
1164 if (m_pwndLineDiffBar)
1165 m_pwndLineDiffBar->Invalidate();
1169 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1171 if (m_pwndLeft)
1172 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1173 if (m_pwndRight)
1174 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1175 if (m_pwndBottom)
1176 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1179 void CBaseView::ScrollAllSide(int delta)
1181 int nNewOffset = m_nOffsetChar;
1182 nNewOffset += delta;
1183 int nMaxLineLength = GetMaxLineLength();
1184 if (nNewOffset >= nMaxLineLength)
1185 nNewOffset = nMaxLineLength - 1;
1186 if (nNewOffset < 0)
1187 nNewOffset = 0;
1188 ScrollAllToChar(nNewOffset, TRUE);
1189 if (m_pwndLineDiffBar)
1190 m_pwndLineDiffBar->Invalidate();
1191 UpdateCaret();
1194 void CBaseView::ScrollSide(int delta)
1196 int nNewOffset = m_nOffsetChar;
1197 nNewOffset += delta;
1198 int nMaxLineLength = GetMaxLineLength();
1199 if (nNewOffset >= nMaxLineLength)
1200 nNewOffset = nMaxLineLength - 1;
1201 if (nNewOffset < 0)
1202 nNewOffset = 0;
1203 ScrollToChar(nNewOffset, TRUE);
1204 if (m_pwndLineDiffBar)
1205 m_pwndLineDiffBar->Invalidate();
1206 UpdateCaret();
1209 void CBaseView::ScrollVertical(short zDelta)
1211 const int nLineCount = GetLineCount();
1212 int nTopLine = m_nTopLine;
1213 nTopLine -= (zDelta/30);
1214 if (nTopLine < 0)
1215 nTopLine = 0;
1216 if (nTopLine >= nLineCount)
1217 nTopLine = nLineCount - 1;
1218 ScrollToLine(nTopLine, TRUE);
1221 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1223 if (m_nTopLine != nNewTopLine)
1225 if (nNewTopLine < 0)
1226 nNewTopLine = 0;
1228 int nScrollLines = m_nTopLine - nNewTopLine;
1230 m_nTopLine = nNewTopLine;
1231 CRect rcScroll;
1232 GetClientRect(&rcScroll);
1233 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1234 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1235 UpdateWindow();
1236 if (bTrackScrollBar)
1237 RecalcVertScrollBar(TRUE);
1238 UpdateCaret();
1243 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1245 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1247 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1249 int nViewLine = GetViewLineForScreen(nLineIndex);
1250 HICON icon = nullptr;
1251 ASSERT(nViewLine < static_cast<int>(m_ScreenedViewLine.size()));
1252 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1253 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1255 DiffStates state = m_pViewData->GetState(nViewLine);
1256 switch (state)
1258 case DIFFSTATE_ADDED:
1259 case DIFFSTATE_THEIRSADDED:
1260 case DIFFSTATE_YOURSADDED:
1261 case DIFFSTATE_IDENTICALADDED:
1262 case DIFFSTATE_CONFLICTADDED:
1263 eIcon = TScreenedViewLine::ICN_ADD;
1264 break;
1265 case DIFFSTATE_REMOVED:
1266 case DIFFSTATE_THEIRSREMOVED:
1267 case DIFFSTATE_YOURSREMOVED:
1268 case DIFFSTATE_IDENTICALREMOVED:
1269 eIcon = TScreenedViewLine::ICN_REMOVED;
1270 break;
1271 case DIFFSTATE_CONFLICTED:
1272 eIcon = TScreenedViewLine::ICN_CONFLICT;
1273 break;
1274 case DIFFSTATE_CONFLICTED_IGNORED:
1275 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1276 break;
1277 case DIFFSTATE_EDITED:
1278 eIcon = TScreenedViewLine::ICN_EDIT;
1279 break;
1280 default:
1281 break;
1283 bool bIdentical = false;
1284 int blockstart = -1;
1285 int blockend = -1;
1286 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1288 if (bIdentical)
1289 eIcon = TScreenedViewLine::ICN_SAME;
1290 else
1291 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1292 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1294 if (nViewLine > blockstart)
1295 Invalidate(); // redraw the upper icons since they're now changing
1296 while (blockstart <= blockend)
1297 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1300 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1301 eIcon = TScreenedViewLine::ICN_MOVED;
1302 if (m_pViewData->GetMarked(nViewLine))
1303 eIcon = TScreenedViewLine::ICN_MARKED;
1304 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1306 switch (eIcon)
1308 case TScreenedViewLine::ICN_UNKNOWN:
1309 case TScreenedViewLine::ICN_NONE:
1310 break;
1311 case TScreenedViewLine::ICN_SAME:
1312 icon = m_hEqualIcon;
1313 break;
1314 case TScreenedViewLine::ICN_EDIT:
1315 icon = m_hEditedIcon;
1316 break;
1317 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1318 icon = m_hWhitespaceBlockIcon;
1319 break;
1320 case TScreenedViewLine::ICN_ADD:
1321 icon = m_hAddedIcon;
1322 break;
1323 case TScreenedViewLine::ICN_CONFLICT:
1324 icon = m_hConflictedIcon;
1325 break;
1326 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1327 icon = m_hConflictedIgnoredIcon;
1328 break;
1329 case TScreenedViewLine::ICN_REMOVED:
1330 icon = m_hRemovedIcon;
1331 break;
1332 case TScreenedViewLine::ICN_MOVED:
1333 icon = m_hMovedIcon;
1334 break;
1335 case TScreenedViewLine::ICN_MARKED:
1336 icon = m_hMarkedIcon;
1337 break;
1340 int iconWidth = GetSystemMetrics(SM_CXSMICON);
1341 int iconHeight = GetSystemMetrics(SM_CYSMICON);
1342 if (icon)
1344 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height() - iconHeight) / 2, icon, iconWidth, iconHeight, 0, nullptr, DI_NORMAL);
1346 if ((m_bViewLinenumbers)&&(m_nDigits))
1348 int nSubLine = GetSubLineOffset(nLineIndex);
1349 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1350 CString sLinenumber;
1351 if (bIsFirstSubline)
1353 CString sLinenumberFormat;
1354 int nLineNumber = GetLineNumber(nLineIndex);
1355 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1357 // TODO: do not show if there is no number hidden
1358 // TODO: show number if there is only one
1359 sLinenumberFormat.Format(L"%%%ds", m_nDigits);
1360 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? L"↕⁞" : L"⁞"); // alternative …
1362 else if (nLineNumber >= 0)
1364 sLinenumberFormat.Format(L"%%%dd", m_nDigits);
1365 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1367 else if (m_pMainFrame->m_bWrapLines)
1369 sLinenumberFormat.Format(L"%%%ds", m_nDigits);
1370 sLinenumber.Format(sLinenumberFormat, L"·");
1372 if (!sLinenumber.IsEmpty())
1374 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1375 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1377 pdc->SelectObject(GetFont());
1378 pdc->ExtTextOut(rect.left + iconWidth + 2, rect.top, ETO_CLIPPED, &rect, sLinenumber, nullptr);
1385 int CBaseView::GetMarginWidth()
1387 int marginWidth = GetSystemMetrics(SM_CXSMICON) + 2 + 2;
1389 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1391 if (m_nDigits <= 0)
1393 int nLength = m_pViewData->GetCount();
1394 // find out how many digits are needed to show the highest line number
1395 CString sMax;
1396 sMax.Format(L"%d", nLength);
1397 m_nDigits = sMax.GetLength();
1399 int nWidth = GetCharWidth();
1400 marginWidth += (m_nDigits * nWidth) + 2;
1403 return marginWidth;
1406 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1408 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1409 COLORREF crBk, crFg;
1410 if (IsBottomViewGood())
1412 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1413 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1415 else
1417 DiffStates state = DIFFSTATE_REMOVED;
1418 if (this == m_pwndRight)
1420 state = DIFFSTATE_ADDED;
1422 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1424 pdc->SetBkColor(crBk);
1425 pdc->FillSolidRect(textrect, crBk);
1427 pdc->SetTextColor(crFg);
1429 pdc->SelectObject(GetFont(FALSE, TRUE));
1431 CString sViewTitle;
1432 if (IsModified())
1434 sViewTitle = L"* " + m_sWindowName;
1436 else
1438 sViewTitle = m_sWindowName;
1440 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1441 if (nStringLength > rect.Width())
1443 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1444 sViewTitle = m_sWindowName.Mid(offset);
1446 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1447 rect.top + (HEADERHEIGHT / 2), ETO_CLIPPED, textrect, sViewTitle, nullptr);
1448 if (this->GetFocus() == this)
1449 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1450 else
1451 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1454 void CBaseView::OnDraw(CDC * pDC)
1456 CRect rcClient;
1457 GetClientRect(rcClient);
1459 int nLineCount = GetLineCount();
1460 int nLineHeight = GetLineHeight();
1462 CDC cacheDC;
1463 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1464 if (!m_pCacheBitmap)
1466 m_pCacheBitmap = new CBitmap;
1467 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1469 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1471 DrawHeader(pDC, rcClient);
1473 CRect rcLine;
1474 rcLine = rcClient;
1475 rcLine.top += nLineHeight+HEADERHEIGHT;
1476 rcLine.bottom = rcLine.top + nLineHeight;
1477 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1478 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1480 int nCurrentLine = m_nTopLine;
1481 bool bBeyondFileLineCached = false;
1482 while (rcLine.top < rcClient.bottom)
1484 if (nCurrentLine < nLineCount)
1486 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1487 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1488 bBeyondFileLineCached = false;
1490 else if (!bBeyondFileLineCached)
1492 DrawMargin(&cacheDC, rcCacheMargin, -1);
1493 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1494 bBeyondFileLineCached = true;
1497 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1499 nCurrentLine ++;
1500 rcLine.OffsetRect(0, nLineHeight);
1503 cacheDC.SelectObject(pOldBitmap);
1504 cacheDC.DeleteDC();
1507 bool CBaseView::IsStateConflicted(DiffStates state)
1509 switch (state)
1511 case DIFFSTATE_CONFLICTED:
1512 case DIFFSTATE_CONFLICTED_IGNORED:
1513 case DIFFSTATE_CONFLICTEMPTY:
1514 case DIFFSTATE_CONFLICTADDED:
1515 return true;
1517 return false;
1520 bool CBaseView::IsStateEmpty(DiffStates state)
1522 switch (state)
1524 case DIFFSTATE_CONFLICTEMPTY:
1525 case DIFFSTATE_UNKNOWN:
1526 case DIFFSTATE_EMPTY:
1527 return true;
1529 return false;
1532 bool CBaseView::IsStateRemoved(DiffStates state)
1534 switch (state)
1536 case DIFFSTATE_REMOVED:
1537 case DIFFSTATE_THEIRSREMOVED:
1538 case DIFFSTATE_YOURSREMOVED:
1539 case DIFFSTATE_IDENTICALREMOVED:
1540 return true;
1542 return false;
1545 DiffStates CBaseView::ResolveState(DiffStates state)
1547 if (IsStateConflicted(state))
1549 if (state == DIFFSTATE_CONFLICTEMPTY)
1550 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1551 else
1552 return DIFFSTATE_CONFLICTRESOLVED;
1554 return state;
1558 bool CBaseView::IsLineEmpty(int nLineIndex)
1560 if (m_pViewData == 0)
1561 return FALSE;
1562 int nViewLine = GetViewLineForScreen(nLineIndex);
1563 return IsViewLineEmpty(nViewLine);
1566 bool CBaseView::IsViewLineEmpty(int nViewLine)
1568 if (m_pViewData == 0)
1569 return FALSE;
1570 const DiffStates state = m_pViewData->GetState(nViewLine);
1571 return IsStateEmpty(state);
1574 bool CBaseView::IsLineRemoved(int nLineIndex)
1576 if (m_pViewData == 0)
1577 return FALSE;
1578 int nViewLine = GetViewLineForScreen(nLineIndex);
1579 return IsViewLineRemoved(nViewLine);
1582 bool CBaseView::IsViewLineRemoved(int nViewLine)
1584 if (m_pViewData == 0)
1585 return FALSE;
1586 const DiffStates state = m_pViewData->GetState(nViewLine);
1587 return IsStateRemoved(state);
1590 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1592 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1595 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1597 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1600 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1602 if (origin.x < (rc.left - GetCharWidth() +1))
1603 return;
1604 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1605 return;
1606 int viewLine = GetViewLineForScreen(nLineIndex);
1607 EOL ending = m_pViewData->GetLineEnding(viewLine);
1608 if (m_bIconLFs)
1610 HICON hEndingIcon = nullptr;
1611 switch (ending)
1613 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1614 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1615 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1616 default: return;
1618 // If EOL style has changed, color end-of-line markers as inline differences.
1620 m_bShowInlineDiff && m_pOtherViewData &&
1621 (viewLine < m_pOtherViewData->GetCount()) &&
1622 (ending != EOL_NOENDING) &&
1623 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1624 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1627 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1630 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), 0, nullptr, DI_NORMAL);
1632 else
1634 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1635 CPen * oldpen = pDC->SelectObject(&pen);
1636 int yMiddle = origin.y + rc.Height()/2;
1637 int xMiddle = origin.x+GetCharWidth()/2;
1638 bool bMultiline = false;
1639 if ((m_Screen2View.size() > nLineIndex + 1) && (GetViewLineForScreen(nLineIndex + 1) == viewLine))
1641 if (GetLineLength(nLineIndex+1))
1643 // multiline
1644 bMultiline = true;
1645 pDC->MoveTo(origin.x, yMiddle - CDPIAware::Instance().ScaleY(2));
1646 pDC->LineTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle - CDPIAware::Instance().ScaleY(2));
1647 pDC->LineTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle + CDPIAware::Instance().ScaleY(2));
1648 pDC->LineTo(origin.x, yMiddle + CDPIAware::Instance().ScaleY(2));
1650 else if (GetLineLength(nLineIndex) == 0)
1651 bMultiline = true;
1653 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1654 bMultiline = true;
1656 if (!bMultiline)
1658 switch (ending)
1660 case EOL_AUTOLINE:
1661 case EOL_CRLF:
1662 // arrow from top to middle+2, then left
1663 pDC->MoveTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), rc.top + CDPIAware::Instance().ScaleY(1));
1664 pDC->LineTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle);
1665 case EOL_CR:
1666 // arrow from right to left
1667 pDC->MoveTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle);
1668 pDC->LineTo(origin.x, yMiddle);
1669 pDC->LineTo(origin.x + CDPIAware::Instance().ScaleX(4), yMiddle + CDPIAware::Instance().ScaleY(4));
1670 pDC->MoveTo(origin.x, yMiddle);
1671 pDC->LineTo(origin.x + CDPIAware::Instance().ScaleX(4), yMiddle - CDPIAware::Instance().ScaleY(4));
1672 break;
1673 case EOL_LFCR:
1674 // from right-upper to left then down
1675 pDC->MoveTo(origin.x + GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle - CDPIAware::Instance().ScaleY(2));
1676 pDC->LineTo(xMiddle, yMiddle - CDPIAware::Instance().ScaleY(2));
1677 pDC->LineTo(xMiddle, rc.bottom - CDPIAware::Instance().ScaleY(1));
1678 pDC->LineTo(xMiddle + CDPIAware::Instance().ScaleX(4), rc.bottom - CDPIAware::Instance().ScaleY(5));
1679 pDC->MoveTo(xMiddle, rc.bottom - CDPIAware::Instance().ScaleY(1));
1680 pDC->LineTo(xMiddle - CDPIAware::Instance().ScaleX(4), rc.bottom - CDPIAware::Instance().ScaleY(5));
1681 break;
1682 case EOL_LF:
1683 // arrow from top to bottom
1684 pDC->MoveTo(xMiddle, rc.top);
1685 pDC->LineTo(xMiddle, rc.bottom - CDPIAware::Instance().ScaleY(1));
1686 pDC->LineTo(xMiddle + CDPIAware::Instance().ScaleX(4), rc.bottom - CDPIAware::Instance().ScaleY(5));
1687 pDC->MoveTo(xMiddle, rc.bottom - CDPIAware::Instance().ScaleY(1));
1688 pDC->LineTo(xMiddle - CDPIAware::Instance().ScaleX(4), rc.bottom - CDPIAware::Instance().ScaleY(5));
1689 break;
1690 case EOL_FF: // Form Feed, U+000C
1691 case EOL_NEL: // Next Line, U+0085
1692 case EOL_LS: // Line Separator, U+2028
1693 case EOL_PS: // Paragraph Separator, U+2029
1694 // draw a horizontal line at the bottom of this line
1695 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1696 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1697 pDC->LineTo(origin.x, rc.bottom-2);
1698 pDC->LineTo(origin.x+5, rc.bottom-2);
1699 pDC->MoveTo(origin.x, rc.bottom-2);
1700 pDC->LineTo(origin.x+1, rc.bottom-6);
1701 break;
1702 default: // other EOLs
1703 // arrow from top right to bottom left
1704 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1705 pDC->LineTo(origin.x, rc.bottom-1);
1706 pDC->LineTo(origin.x+5, rc.bottom-2);
1707 pDC->MoveTo(origin.x, rc.bottom-1);
1708 pDC->LineTo(origin.x+1, rc.bottom-6);
1709 break;
1710 case EOL_NOENDING:
1711 break;
1714 pDC->SelectObject(oldpen);
1718 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1720 if (!m_bShowSelection)
1721 return;
1723 int nSelBlockStart;
1724 int nSelBlockEnd;
1725 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1726 return;
1728 const int THICKNESS = 2;
1729 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1731 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1732 int nSubLine = GetSubLineOffset(nLineIndex);
1733 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1734 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1736 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1739 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1740 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1742 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1746 void CBaseView::DrawTextLine(
1747 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1749 ASSERT(nLineIndex < GetLineCount());
1750 int nViewLine = GetViewLineForScreen(nLineIndex);
1751 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1753 LineColors lineCols = GetLineColors(nViewLine);
1755 CString sViewLine = GetViewLineChars(nViewLine);
1756 // mark selection
1757 if (m_bShowSelection && HasTextSelection())
1759 // has this line selection ?
1760 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1762 int nViewLineLength = sViewLine.GetLength();
1764 // first suppose the whole line is selected
1765 int selectedStart = 0;
1766 int selectedEnd = nViewLineLength;
1768 // the view line is partially selected
1769 if (m_ptSelectionViewPosStart.y == nViewLine)
1771 selectedStart = m_ptSelectionViewPosStart.x;
1774 if (m_ptSelectionViewPosEnd.y == nViewLine)
1776 selectedEnd = m_ptSelectionViewPosEnd.x;
1778 // apply selection coloring
1779 // First enforce start and end point
1780 lineCols.SplitBlock(selectedStart);
1781 lineCols.SplitBlock(selectedEnd);
1782 // change color of affected parts
1783 long intenseColorScale = m_bFocused ? 70 : 30;
1784 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1785 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1787 auto& second = it->second;
1788 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, second.background);
1789 if (second.shot == second.background)
1790 second.shot = crBk;
1791 second.background = crBk;
1792 second.text = CAppUtils::IntenseColor(intenseColorScale, second.text);
1797 // TODO: remove duplicate from selection and mark
1798 if (!m_sMarkedWord.IsEmpty())
1800 int nMarkLength = m_sMarkedWord.GetLength();
1801 //int nViewLineLength = sViewLine.GetLength();
1802 const TCHAR * text = sViewLine;
1803 const TCHAR * findText = text;
1804 while ((findText = wcsstr(findText, static_cast<LPCTSTR>(m_sMarkedWord)))!=0)
1806 int nMarkStart = static_cast<int>(findText - text);
1807 int nMarkEnd = nMarkStart + nMarkLength;
1808 findText += nMarkLength;
1809 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1810 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1811 if (eLeft == eStart)
1812 continue;
1813 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1814 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1815 if (eRight == eEnd)
1816 continue;
1818 // First enforce start and end point
1819 lineCols.SplitBlock(nMarkStart);
1820 lineCols.SplitBlock(nMarkEnd);
1821 // change color of affected parts
1822 const long int nIntenseColorScale = 200;
1823 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1824 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1826 auto& second = it->second;
1827 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1828 if (second.shot == second.background)
1829 second.shot = crBk;
1830 second.background = crBk;
1831 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1835 if (!m_sFindText.IsEmpty())
1837 int nMarkStart = 0;
1838 int nMarkEnd = 0;
1839 int nStringPos = nMarkStart;
1840 CString searchLine = sViewLine;
1841 if (!m_bMatchCase)
1842 searchLine.MakeLower();
1843 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1845 // First enforce start and end point
1846 lineCols.SplitBlock(nMarkStart+nStringPos);
1847 lineCols.SplitBlock(nMarkEnd+nStringPos);
1848 // change color of affected parts
1849 const long int nIntenseColorScale = 30;
1850 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1851 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1853 auto& second = it->second;
1854 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1855 if (second.shot == second.background)
1856 second.shot = crBk;
1857 second.background = crBk;
1858 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1860 searchLine = searchLine.Mid(nMarkEnd);
1861 nStringPos = nMarkEnd;
1862 nMarkStart = 0;
1863 nMarkEnd = 0;
1867 // @ this point we may cache data for next line which may be same in wrapped mode
1869 int nTextOffset = 0;
1870 int nSubline = GetSubLineOffset(nLineIndex);
1871 for (int n=0; n<nSubline; n++)
1873 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1874 nTextOffset += sLine.GetLength();
1877 CString sLine = GetLineChars(nLineIndex);
1878 int nLineLength = sLine.GetLength();
1879 CString sLineExp = ExpandChars(sLine);
1880 LPCTSTR textExp = sLineExp;
1881 //int nLineLengthExp = sLineExp.GetLength();
1882 int nStartExp = 0;
1883 int nLeft = coords.x;
1884 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1886 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1887 ++itEnd;
1888 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1889 int nEnd = nLineLength;
1890 if (itEnd != lineCols.end())
1892 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1894 int nBlockLength = nEnd - nStart;
1895 if (nBlockLength > 0 && nEnd>=0)
1897 auto& second = itStart->second;
1898 pDC->SetBkColor(second.background);
1899 pDC->SetTextColor(second.text);
1900 int nEndExp = CountExpandedChars(sLine, nEnd);
1901 int nTextLength = nEndExp - nStartExp;
1902 LPCTSTR p_zBlockText = textExp + nStartExp;
1903 SIZE Size;
1904 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1905 int nRight = nLeft + Size.cx;
1906 if ((nRight > rc.left) && (nLeft < rc.right))
1908 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1909 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1910 // is 4094 (4095 doesn't work anymore).
1911 // So we limit the length here to that 4094 chars.
1912 // In case we're scrolled to the right, there's no need to draw the string
1913 // from way outside our window, so we also offset the drawing to the start of the window.
1914 // This reduces the string length as well.
1915 int offset = 0;
1916 int leftcoord = nLeft;
1917 if (nLeft < 0)
1919 int fit = nTextLength;
1920 auto posBuffer = std::make_unique<int[]>(fit);
1921 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1922 int lower = 0, upper = fit - 1;
1925 int middle = (upper + lower + 1) / 2;
1926 int width = posBuffer[middle];
1927 if (rc.left - nLeft < width)
1928 upper = middle - 1;
1929 else
1930 lower = middle;
1931 } while (lower < upper);
1933 offset = lower;
1934 nTextLength -= offset;
1935 leftcoord += lower > 0 ? posBuffer[lower - 1] : 0;
1938 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText + offset, min(nTextLength, 4094), nullptr);
1939 if ((second.shot != second.background) && (itStart->first == nStart + nTextOffset))
1940 pDC->FillSolidRect(nLeft - 1, rc.top, 1, rc.Height(), second.shot);
1942 nLeft = nRight;
1943 coords.x = nRight;
1944 nStartExp = nEndExp;
1949 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1951 if (nLineIndex >= GetLineCount())
1952 nLineIndex = -1;
1953 ASSERT(nLineIndex >= -1);
1955 if ((nLineIndex == -1) || !m_pViewData)
1957 // Draw line beyond the text
1958 COLORREF crBkgnd, crText;
1959 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1960 pDC->FillSolidRect(rc, crBkgnd);
1961 return;
1964 int viewLine = GetViewLineForScreen(nLineIndex);
1965 if (m_pMainFrame->m_bCollapsed)
1967 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1969 COLORREF crBkgnd, crText;
1970 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1971 pDC->FillSolidRect(rc, crBkgnd);
1973 const int THICKNESS = 2;
1974 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1975 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1976 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1977 pDC->SetBkColor(crBkgnd);
1978 CRect rect = rc;
1979 pDC->DrawText(L"{...}", &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1980 return;
1984 DiffStates diffState = m_pViewData->GetState(viewLine);
1985 COLORREF crBkgnd, crText;
1986 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1988 if (diffState == DIFFSTATE_CONFLICTED)
1990 // conflicted lines are shown without 'text' on them
1991 CRect rect = rc;
1992 pDC->FillSolidRect(rc, crBkgnd);
1993 // now draw some faint text patterns
1994 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1995 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1996 DrawBlockLine(pDC, rc, nLineIndex);
1997 return;
2000 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
2001 CString sLine = GetLineChars(nLineIndex);
2002 if (sLine.IsEmpty())
2004 pDC->FillSolidRect(rc, crBkgnd);
2005 DrawBlockLine(pDC, rc, nLineIndex);
2006 DrawLineEnding(pDC, rc, nLineIndex, origin);
2007 return;
2010 CheckOtherView();
2012 // Draw the line
2014 pDC->SelectObject(GetFont(FALSE, FALSE));
2016 DrawTextLine(pDC, rc, nLineIndex, origin);
2018 // draw white space after the end of line
2019 CRect frect = rc;
2020 if (origin.x > frect.left)
2021 frect.left = origin.x;
2022 if (frect.right > frect.left)
2023 pDC->FillSolidRect(frect, crBkgnd);
2025 // draw the whitespace chars
2026 auto pszChars = static_cast<LPCTSTR>(sLine);
2027 if (m_bViewWhitespace)
2029 int xpos = 0;
2030 int nChars = 0;
2031 LPCTSTR pLastSpace = pszChars;
2032 int y = rc.top + (rc.bottom-rc.top)/2;
2033 xpos -= m_nOffsetChar * GetCharWidth();
2035 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2036 while (*pszChars)
2038 switch (*pszChars)
2040 case '\t':
2042 xpos += pDC->GetTextExtent(pLastSpace, static_cast<int>(pszChars - pLastSpace)).cx;
2043 pLastSpace = pszChars + 1;
2044 // draw an arrow
2045 int nSpaces = GetTabSize() - nChars % GetTabSize();
2046 if (xpos + nSpaces * GetCharWidth() > 0)
2048 int xposreal = max(xpos, 0);
2049 if ((xposreal > 0) || (nSpaces > 0))
2051 CPen * oldPen = pDC->SelectObject(&pen);
2052 pDC->MoveTo(xposreal + rc.left + CDPIAware::Instance().ScaleX(2), y);
2053 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - CDPIAware::Instance().ScaleX(2), y);
2054 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - CDPIAware::Instance().ScaleX(6), y - CDPIAware::Instance().ScaleY(4));
2055 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - CDPIAware::Instance().ScaleX(2), y);
2056 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - CDPIAware::Instance().ScaleX(6), y + CDPIAware::Instance().ScaleY(4));
2057 pDC->SelectObject(oldPen);
2060 xpos += nSpaces * GetCharWidth();
2061 nChars += nSpaces;
2063 break;
2064 case ' ':
2066 xpos += pDC->GetTextExtent(pLastSpace, static_cast<int>(pszChars - pLastSpace)).cx;
2067 pLastSpace = pszChars + 1;
2068 if (xpos >= 0)
2070 const int cxWhitespace = CDPIAware::Instance().ScaleX(2);
2071 const int cyWhitespace = CDPIAware::Instance().ScaleY(2);
2072 // draw 2-logical pixel rectangle, like Scintilla editor.
2073 pDC->FillSolidRect(xpos + rc.left + GetCharWidth() / 2 - cxWhitespace/2, y, cxWhitespace, cyWhitespace, m_WhiteSpaceFg);
2075 xpos += GetCharWidth();
2076 nChars++;
2078 break;
2079 default:
2080 nChars++;
2081 break;
2083 pszChars++;
2086 DrawBlockLine(pDC, rc, nLineIndex);
2087 if (origin.x >= rc.left)
2088 DrawLineEnding(pDC, rc, nLineIndex, origin);
2091 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2093 if (nCount <= 0)
2095 line.Empty();
2096 return;
2099 int nTabSize = GetTabSize();
2101 int nActualOffset = CountExpandedChars(sLine, nOffset);
2103 auto pszChars = static_cast<LPCTSTR>(sLine);
2104 pszChars += nOffset;
2105 int nLength = nCount;
2107 int nTabCount = 0;
2108 for (int i=0; i<nLength; i++)
2110 if (pszChars[i] == L'\t')
2111 nTabCount ++;
2114 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2115 int nCurPos = 0;
2116 if (nTabCount > 0 || m_bViewWhitespace)
2118 for (int i=0; i<nLength; i++)
2120 if (pszChars[i] == L'\t')
2122 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2123 while (nSpaces > 0)
2125 pszBuf[nCurPos ++] = L' ';
2126 nSpaces --;
2129 else
2131 pszBuf[nCurPos] = pszChars[i];
2132 nCurPos ++;
2136 else
2138 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2139 nCurPos = nLength;
2141 pszBuf[nCurPos] = 0;
2142 line.ReleaseBuffer();
2145 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2147 CString sRet;
2148 int nLength = sLine.GetLength();
2149 ExpandChars(sLine, nOffset, nLength, sRet);
2150 return sRet;
2153 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2155 int nTabSize = GetTabSize();
2157 int nActualOffset = 0;
2158 for (int i=0; i<nLength; i++)
2160 if (sLine[i] == L'\t')
2161 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2162 else
2163 nActualOffset ++;
2165 return nActualOffset;
2168 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2170 if (m_pwndLeft)
2171 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2172 if (m_pwndRight)
2173 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2174 if (m_pwndBottom)
2175 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2176 if (m_pwndLocator)
2177 m_pwndLocator->Invalidate();
2180 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2182 //almost the same as ScrollAllToLine, but try to put the line in the
2183 //middle of the view, not on top
2184 int nNewTopLine = nNewLine - GetScreenLines()/2;
2185 if (nNewTopLine < 0)
2186 nNewTopLine = 0;
2187 if (nNewTopLine >= m_Screen2View.size())
2188 nNewTopLine = m_Screen2View.size() - 1;
2189 if (bAll)
2190 ScrollAllToLine(nNewTopLine);
2191 else
2192 ScrollToLine(nNewTopLine);
2195 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2197 return TRUE;
2200 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2202 if (CView::OnCreate(lpCreateStruct) == -1)
2203 return -1;
2205 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2206 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2207 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2208 m_lfBaseFont.lfHeight = 0;
2209 m_lfBaseFont.lfWeight = FW_NORMAL;
2210 m_lfBaseFont.lfItalic = FALSE;
2211 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2212 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2213 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2214 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2215 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2217 return 0;
2220 void CBaseView::OnDestroy()
2222 CView::OnDestroy();
2223 DeleteFonts();
2224 ReleaseBitmap();
2227 void CBaseView::OnSize(UINT nType, int cx, int cy)
2229 CView::OnSize(nType, cx, cy);
2230 ReleaseBitmap();
2232 m_nScreenLines = -1;
2233 m_nScreenChars = -1;
2234 if (m_nLastScreenChars != GetScreenChars())
2236 BuildAllScreen2ViewVector();
2237 m_nLastScreenChars = m_nScreenChars;
2238 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2240 // if we're in wrap mode, the line wrapping most likely changed
2241 // and that means we have to redraw the whole window, not just the
2242 // scrolled part.
2243 Invalidate(FALSE);
2245 else
2247 // make sure the view header is redrawn
2248 CRect rcScroll;
2249 GetClientRect(&rcScroll);
2250 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2251 InvalidateRect(&rcScroll, FALSE);
2254 else
2256 // make sure the view header is redrawn
2257 CRect rcScroll;
2258 GetClientRect(&rcScroll);
2259 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2260 InvalidateRect(&rcScroll, FALSE);
2262 UpdateLocator();
2263 RecalcVertScrollBar();
2264 RecalcHorzScrollBar();
2266 UpdateCaret();
2269 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2271 if (m_pwndLeft)
2272 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2273 if (m_pwndRight)
2274 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2275 if (m_pwndBottom)
2276 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2277 if (m_pwndLocator)
2278 m_pwndLocator->Invalidate();
2279 return CView::OnMouseWheel(nFlags, zDelta, pt);
2282 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2284 if (m_pwndLeft)
2285 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2286 if (m_pwndRight)
2287 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2288 if (m_pwndBottom)
2289 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2290 if (m_pwndLocator)
2291 m_pwndLocator->Invalidate();
2294 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2296 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2297 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2299 if (bControl || bShift)
2301 if (m_pMainFrame->m_bWrapLines)
2302 return;
2303 // Ctrl-Wheel scrolls sideways
2304 ScrollSide(-zDelta/30);
2306 else
2308 ScrollVertical(zDelta);
2312 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2314 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2315 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2317 if (bControl || bShift)
2319 // Ctrl-H-Wheel scrolls vertical
2320 ScrollVertical(zDelta);
2322 else
2324 if (m_pMainFrame->m_bWrapLines)
2325 return;
2326 // Ctrl-Wheel scrolls sideways
2327 ScrollSide(zDelta/30);
2331 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2333 if (nHitTest == HTCLIENT)
2335 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2337 if (m_nMouseLine < m_Screen2View.size())
2339 if (m_nMouseLine >= 0)
2341 int viewLine = GetViewLineForScreen(m_nMouseLine);
2342 if (viewLine < m_pViewData->GetCount())
2344 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2346 ::SetCursor(::LoadCursor(nullptr, IDC_HAND));
2347 return TRUE;
2353 if (m_mouseInMargin)
2355 ::SetCursor(m_margincursor);
2356 return TRUE;
2358 if (m_nMouseLine >= 0)
2360 ::SetCursor(::LoadCursor(nullptr, IDC_IBEAM)); // Set To Edit Cursor
2361 return TRUE;
2364 ::SetCursor(::LoadCursor(nullptr, IDC_ARROW)); // Set To Arrow Cursor
2365 return TRUE;
2367 return CView::OnSetCursor(pWnd, nHitTest, message);
2370 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2372 CView::OnKillFocus(pNewWnd);
2373 m_bFocused = FALSE;
2374 UpdateCaret();
2375 Invalidate();
2378 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2380 CView::OnSetFocus(pOldWnd);
2381 m_bFocused = TRUE;
2382 UpdateCaret();
2383 Invalidate();
2386 int CBaseView::GetLineFromPoint(CPoint point)
2388 ScreenToClient(&point);
2389 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2392 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2394 CRect rcClient;
2395 GetClientRect(rcClient);
2396 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight + HEADERHEIGHT);
2398 CRect borderrect(rcClient.left, rcClient.top + m_nLineHeight + HEADERHEIGHT, 0, rcClient.bottom);
2400 CPoint ptLocal = point;
2401 ScreenToClient(&ptLocal);
2403 if (textrect.PtInRect(ptLocal) || borderrect.PtInRect(ptLocal))
2405 // inside the header part of the view (showing the filename)
2406 if (IsViewGood(m_pwndBottom))
2408 CString temp;
2409 if (this == m_pwndLeft)
2411 CIconMenu popup;
2412 if (!popup.CreatePopupMenu())
2413 return;
2415 temp.LoadString(IDS_HEADER_DIFFLEFTTOBASE);
2416 popup.AppendMenu(MF_STRING | MF_ENABLED, 10, temp);
2417 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2418 if (cmd == 10)
2419 m_pMainFrame->DiffLeftToBase();
2421 if (this == m_pwndRight)
2423 CIconMenu popup;
2424 if (!popup.CreatePopupMenu())
2425 return;
2427 temp.LoadString(IDS_HEADER_DIFFRIGHTTOBASE);
2428 popup.AppendMenu(MF_STRING | MF_ENABLED, 10, temp);
2429 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2430 if (cmd == 10)
2431 m_pMainFrame->DiffRightToBase();
2434 return;
2437 if (!this->IsWindowVisible())
2438 return;
2440 CIconMenu popup;
2441 if (!popup.CreatePopupMenu())
2442 return;
2444 AddContextItems(popup, state);
2446 CMenu popupEols;
2447 CMenu popupUnicode;
2448 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2449 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2450 if (IsWritable())
2452 CString temp;
2453 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2454 temp.LoadString(IDS_EDIT_TAB2SPACE);
2455 popup.AppendMenu(MF_STRING | (oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_TABTOSPACES, temp);
2456 temp.LoadString(IDS_EDIT_SPACE2TAB);
2457 popup.AppendMenu(MF_STRING | (oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_SPACESTOTABS, temp);
2458 temp.LoadString(IDS_EDIT_TRIM);
2459 popup.AppendMenu(MF_STRING | (oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2461 // add eol submenu
2462 if (!popupEols.CreatePopupMenu())
2463 return;
2465 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2466 for (int i = 1; i < _countof(eolArray); i++)
2468 temp = GetEolName(eolArray[i]);
2469 bool bChecked = (eEolType == eolArray[i]);
2470 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2473 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2474 popup.AppendMenuW(MF_POPUP | MF_ENABLED, reinterpret_cast<UINT_PTR>(popupEols.GetSafeHmenu()), temp);
2476 // add encoding submenu
2477 if (!popupUnicode.CreatePopupMenu())
2478 return;
2479 for (int i = 0; i < _countof(uctArray); i++)
2481 temp = CFileTextLines::GetEncodingName(uctArray[i]);
2482 bool bChecked = (m_texttype == uctArray[i]);
2483 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2485 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2486 popup.AppendMenuW(MF_POPUP | MF_ENABLED, reinterpret_cast<UINT_PTR>(popupUnicode.GetSafeHmenu()), temp);
2490 CompensateForKeyboard(point);
2492 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2493 ResetUndoStep();
2494 if (cmd >= nEncodingCommandBase && (cmd < nEncodingCommandBase + static_cast<int>(_countof(uctArray))))
2496 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2498 if (cmd >= nEolCommandBase && (cmd < nEolCommandBase + static_cast<int>(_countof(eolArray))))
2500 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2501 SaveUndoStep();
2503 switch (cmd)
2505 // 2-pane view commands; target is right view
2506 case POPUPCOMMAND_USELEFTBLOCK:
2507 m_pwndRight->UseLeftBlock();
2508 break;
2509 case POPUPCOMMAND_USELEFTFILE:
2510 m_pwndRight->UseLeftFile();
2511 break;
2512 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2513 m_pwndRight->UseBothLeftFirst();
2514 break;
2515 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2516 m_pwndRight->UseBothRightFirst();
2517 break;
2518 case POPUPCOMMAND_MARKBLOCK:
2519 m_pwndRight->MarkBlock(true);
2520 break;
2521 case POPUPCOMMAND_UNMARKBLOCK:
2522 m_pwndRight->MarkBlock(false);
2523 break;
2524 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2525 m_pwndRight->LeaveOnlyMarkedBlocks();
2526 break;
2527 // 2-pane view multiedit commands; target is left view
2528 case POPUPCOMMAND_PREPENDFROMRIGHT:
2529 if (!m_pwndLeft->IsReadonly())
2530 m_pwndLeft->UseBothRightFirst();
2531 break;
2532 case POPUPCOMMAND_REPLACEBYRIGHT:
2533 if (!m_pwndLeft->IsReadonly())
2534 m_pwndLeft->UseRightBlock();
2535 break;
2536 case POPUPCOMMAND_APPENDFROMRIGHT:
2537 if (!m_pwndLeft->IsReadonly())
2538 m_pwndLeft->UseBothLeftFirst();
2539 break;
2540 case POPUPCOMMAND_USERIGHTFILE:
2541 m_pwndLeft->UseRightFile();
2542 break;
2543 // 3-pane view commands; target is bottom view
2544 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2545 m_pwndBottom->UseBothRightFirst();
2546 break;
2547 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2548 m_pwndBottom->UseBothLeftFirst();
2549 break;
2550 case POPUPCOMMAND_USEYOURBLOCK:
2551 m_pwndBottom->UseRightBlock();
2552 break;
2553 case POPUPCOMMAND_USEYOURFILE:
2554 m_pwndBottom->UseRightFile();
2555 break;
2556 case POPUPCOMMAND_USETHEIRBLOCK:
2557 m_pwndBottom->UseLeftBlock();
2558 break;
2559 case POPUPCOMMAND_USETHEIRFILE:
2560 m_pwndBottom->UseLeftFile();
2561 break;
2562 // copy, cut and paste commands
2563 case ID_EDIT_COPY:
2564 OnEditCopy();
2565 break;
2566 case ID_EDIT_CUT:
2567 OnEditCut();
2568 break;
2569 case ID_EDIT_PASTE:
2570 OnEditPaste();
2571 break;
2572 // white chars manipulations
2573 case POPUPCOMMAND_TABTOSPACES:
2574 ConvertTabToSpaces();
2575 break;
2576 case POPUPCOMMAND_SPACESTOTABS:
2577 Tabularize();
2578 break;
2579 case POPUPCOMMAND_REMOVETRAILWHITES:
2580 RemoveTrailWhiteChars();
2581 break;
2582 default:
2583 return;
2584 } // switch (cmd)
2585 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2586 return;
2589 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2591 if (!m_pViewData)
2592 return;
2594 int nViewBlockStart = -1;
2595 int nViewBlockEnd = -1;
2596 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2597 if ((point.x != -1) && (point.y != -1))
2599 int nLine = GetLineFromPoint(point)-1;
2600 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2602 int nViewLine = GetViewLineForScreen(nLine);
2603 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2605 ClearSelection(); // Clear text-copy selection
2607 nViewBlockStart = nViewLine;
2608 nViewBlockEnd = nViewLine;
2609 DiffStates state = m_pViewData->GetState(nViewLine);
2610 while (nViewBlockStart > 0)
2612 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2613 if (!LinesInOneChange(-1, state, lineState))
2614 break;
2615 nViewBlockStart--;
2618 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2620 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2621 if (!LinesInOneChange(1, state, lineState))
2622 break;
2623 nViewBlockEnd++;
2626 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2627 UpdateCaretPosition(SetupPoint(0, nViewLine));
2632 // FixSelection(); fix selection range
2633 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2634 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2636 DiffStates state = DIFFSTATE_UNKNOWN;
2637 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2639 // find a more 'relevant' state in the selection
2640 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2642 state = m_pViewData->GetState(i);
2643 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2644 break;
2647 OnContextMenu(point, state);
2650 void CBaseView::RefreshViews()
2652 if (m_pwndLeft)
2654 m_pwndLeft->UpdateStatusBar();
2655 m_pwndLeft->UpdateCaret();
2656 m_pwndLeft->Invalidate();
2658 if (m_pwndRight)
2660 m_pwndRight->UpdateStatusBar();
2661 m_pwndRight->UpdateCaret();
2662 m_pwndRight->Invalidate();
2664 if (m_pwndBottom)
2666 m_pwndBottom->UpdateStatusBar();
2667 m_pwndBottom->UpdateCaret();
2668 m_pwndBottom->Invalidate();
2670 if (m_pwndLocator)
2671 m_pwndLocator->Invalidate();
2674 void CBaseView::GoToFirstDifference()
2676 SetCaretToFirstViewLine();
2677 SelectNextBlock(1, false, false);
2680 void CBaseView::GoToFirstConflict()
2682 SetCaretToFirstViewLine();
2683 SelectNextBlock(1, true, false);
2686 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2688 ClearSelection();
2689 SetupAllViewSelection(nStart, max(nStart, nEnd));
2691 UpdateCaretViewPosition(SetupPoint(0, nStart));
2692 Invalidate();
2695 void CBaseView::SetupAllViewSelection(int start, int end)
2697 SetupViewSelection(m_pwndBottom, start, end);
2698 SetupViewSelection(m_pwndLeft, start, end);
2699 SetupViewSelection(m_pwndRight, start, end);
2702 void CBaseView::SetupAllSelection(int start, int end)
2704 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2707 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2709 void CBaseView::SetupSelection(int start, int end)
2711 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2714 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2716 if (!IsViewGood(view))
2717 return;
2718 view->SetupViewSelection(start, end);
2721 void CBaseView::SetupViewSelection(int start, int end)
2723 // clear text selection before setting line selection ?
2724 m_nSelViewBlockStart = start;
2725 m_nSelViewBlockEnd = end;
2726 Invalidate();
2730 void CBaseView::OnMergePreviousconflict()
2732 SelectNextBlock(-1, true);
2735 void CBaseView::OnMergeNextconflict()
2737 SelectNextBlock(1, true);
2740 void CBaseView::OnMergeNextdifference()
2742 SelectNextBlock(1, false);
2745 void CBaseView::OnMergePreviousdifference()
2747 SelectNextBlock(-1, false);
2750 bool CBaseView::HasNextConflict()
2752 return SelectNextBlock(1, true, true, true);
2755 bool CBaseView::HasPrevConflict()
2757 return SelectNextBlock(-1, true, true, true);
2760 bool CBaseView::HasNextDiff()
2762 return SelectNextBlock(1, false, true, true);
2765 bool CBaseView::HasPrevDiff()
2767 return SelectNextBlock(-1, false, true, true);
2770 bool CBaseView::LinesInOneChange(int direction,
2771 DiffStates initialLineState, DiffStates currentLineState)
2773 // Checks whether all the adjacent lines starting from the initial line
2774 // and up to the current line form the single change
2776 // First of all, if the two lines have identical states, they surely
2777 // belong to one change.
2778 if (initialLineState == currentLineState)
2779 return true;
2781 // Either we move down and initial line state is "added" or "removed" and
2782 // current line state is "empty"...
2783 if (direction > 0)
2785 if (currentLineState == DIFFSTATE_EMPTY)
2787 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2788 return true;
2790 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2791 return true;
2793 // ...or we move up and initial line state is "empty" and current line
2794 // state is "added" or "removed".
2795 if (direction < 0)
2797 if (initialLineState == DIFFSTATE_EMPTY)
2799 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2800 return true;
2802 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2803 return true;
2805 return false;
2808 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2810 if (! m_pViewData)
2811 return false;
2813 const int linesCount = m_Screen2View.size();
2814 if(linesCount == 0)
2815 return false;
2817 int nCenterPos = GetCaretPosition().y;
2818 int nLimit = -1;
2819 if (nDirection > 0)
2820 nLimit = linesCount;
2822 if (nCenterPos >= linesCount)
2823 nCenterPos = linesCount-1;
2825 if (bSkipEndOfCurrentBlock)
2827 // Find end of current block
2828 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2829 while (nCenterPos != nLimit)
2831 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2832 if (!LinesInOneChange(nDirection, state, lineState))
2833 break;
2834 nCenterPos += nDirection;
2838 // Find next diff/conflict block
2839 while (nCenterPos != nLimit)
2841 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2842 if (!bConflict &&
2843 (linestate != DIFFSTATE_NORMAL) &&
2844 (linestate != DIFFSTATE_UNKNOWN) &&
2845 (linestate != DIFFSTATE_FILTEREDDIFF))
2847 break;
2849 if (bConflict &&
2850 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2851 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2852 (linestate == DIFFSTATE_CONFLICTED) ||
2853 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2855 break;
2858 nCenterPos += nDirection;
2860 if (nCenterPos == nLimit)
2861 return false;
2862 if (dryrun)
2863 return (nCenterPos != nLimit);
2865 // Find end of new block
2866 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2867 int nBlockEnd = nCenterPos;
2868 const int maxAllowedLine = nLimit-nDirection;
2869 while (nBlockEnd != maxAllowedLine)
2871 const int lineIndex = nBlockEnd + nDirection;
2872 if (lineIndex >= linesCount)
2873 break;
2874 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2875 if (!LinesInOneChange(nDirection, state, lineState))
2876 break;
2877 nBlockEnd += nDirection;
2880 int nTopPos = nCenterPos - (GetScreenLines()/2);
2881 if (nTopPos < 0)
2882 nTopPos = 0;
2884 POINT ptCaretPos = {0, nCenterPos};
2885 SetCaretPosition(ptCaretPos);
2886 ClearSelection();
2887 if (nDirection > 0)
2888 SetupAllSelection(nCenterPos, nBlockEnd);
2889 else
2890 SetupAllSelection(nBlockEnd, nCenterPos);
2892 ScrollAllToLine(nTopPos, FALSE);
2893 RecalcAllVertScrollBars(TRUE);
2894 SetCaretToLineStart();
2895 EnsureCaretVisible();
2896 OnNavigateNextinlinediff();
2898 UpdateViewsCaretPosition();
2899 UpdateCaret();
2900 ShowDiffLines(nCenterPos);
2901 return true;
2904 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2906 if (pNMHDR->idFrom != reinterpret_cast<UINT_PTR>(m_hWnd))
2907 return FALSE;
2909 CString strTipText;
2910 strTipText = m_sWindowName + L"\r\n" + m_sFullFilePath;
2912 DWORD pos = GetMessagePos();
2913 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2914 ScreenToClient(&point);
2915 const int nLine = GetButtonEventLineIndex(point);
2917 if (nLine >= 0)
2919 int nViewLine = GetViewLineForScreen(nLine);
2920 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2922 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2923 if (movedIndex >= 0)
2925 if (m_pViewData->IsMovedFrom(nViewLine))
2927 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2929 else
2931 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2938 *pResult = 0;
2939 if (strTipText.IsEmpty())
2940 return TRUE;
2942 // need to handle both ANSI and UNICODE versions of the message
2943 if (pNMHDR->code == TTN_NEEDTEXTA)
2945 auto pTTTA = reinterpret_cast<TOOLTIPTEXTA*>(pNMHDR);
2946 pTTTA->lpszText = m_szTip;
2947 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2949 else
2951 auto pTTTW = reinterpret_cast<TOOLTIPTEXTW*>(pNMHDR);
2952 lstrcpyn(m_wszTip, strTipText, min(strTipText.GetLength() + 1, static_cast<int>(_countof(m_wszTip)) - 1));
2953 pTTTW->lpszText = m_wszTip;
2956 return TRUE; // message was handled
2959 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2961 CRect rcClient;
2962 GetClientRect(rcClient);
2963 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2965 int marginwidth = GetSystemMetrics(SM_CXSMICON) + 2 + 2;
2966 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2968 marginwidth += (m_nDigits * m_nCharWidth) + 2;
2970 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2972 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2974 // inside the header part of the view (showing the filename)
2975 pTI->hwnd = this->m_hWnd;
2976 this->GetClientRect(&pTI->rect);
2977 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2978 pTI->uId = reinterpret_cast<UINT_PTR>(m_hWnd);
2979 pTI->lpszText = LPSTR_TEXTCALLBACK;
2981 // we want multi line tooltips
2982 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2983 if (pToolTip->GetSafeHwnd())
2984 pToolTip->SetMaxTipWidth(SHRT_MAX);
2986 return (textrect.PtInRect(point) ? 1 : 2);
2989 return -1;
2992 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2994 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2995 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2997 switch (nChar)
2999 case VK_TAB:
3000 if (bControl)
3002 if (this==m_pwndLeft)
3004 if (IsViewGood(m_pwndRight))
3006 m_pwndRight->SetFocus();
3008 else if (IsViewGood(m_pwndBottom))
3010 m_pwndBottom->SetFocus();
3013 else if (this==m_pwndRight)
3015 if (IsViewGood(m_pwndBottom))
3017 m_pwndBottom->SetFocus();
3019 else if (IsViewGood(m_pwndLeft))
3021 m_pwndLeft->SetFocus();
3024 else if (this==m_pwndBottom)
3026 if (IsViewGood(m_pwndLeft))
3028 m_pwndLeft->SetFocus();
3030 else if (IsViewGood(m_pwndRight))
3032 m_pwndRight->SetFocus();
3036 break;
3037 case VK_PRIOR:
3039 POINT ptCaretPos = GetCaretPosition();
3040 ptCaretPos.y -= GetScreenLines();
3041 ptCaretPos.y = max(ptCaretPos.y, 0l);
3042 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3043 SetCaretPosition(ptCaretPos);
3044 OnCaretMove(MOVELEFT, bShift);
3045 ShowDiffLines(ptCaretPos.y);
3047 break;
3048 case VK_NEXT:
3050 POINT ptCaretPos = GetCaretPosition();
3051 ptCaretPos.y += GetScreenLines();
3052 if (ptCaretPos.y >= GetLineCount())
3053 ptCaretPos.y = GetLineCount()-1;
3054 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3055 SetCaretPosition(ptCaretPos);
3056 OnCaretMove(MOVERIGHT, bShift);
3057 ShowDiffLines(ptCaretPos.y);
3059 break;
3060 case VK_HOME:
3062 if (bControl)
3064 ScrollAllToLine(0);
3065 ScrollAllToChar(0);
3066 SetCaretToViewStart();
3067 m_nCaretGoalPos = 0;
3068 if (bShift)
3069 AdjustSelection(MOVELEFT);
3070 else
3071 ClearSelection();
3072 UpdateCaret();
3074 else
3076 POINT ptCaretPos = GetCaretPosition();
3077 CString sLine = GetLineChars(ptCaretPos.y);
3078 int pos = 0;
3079 while (pos < sLine.GetLength())
3081 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3082 break;
3083 ++pos;
3085 if (ptCaretPos.x == pos)
3087 SetCaretToLineStart();
3088 m_nCaretGoalPos = 0;
3089 OnCaretMove(MOVERIGHT, bShift);
3090 ScrollAllToChar(0);
3092 else
3094 ptCaretPos.x = pos;
3095 SetCaretAndGoalPosition(ptCaretPos);
3096 OnCaretMove(MOVELEFT, bShift);
3100 break;
3101 case VK_END:
3103 if (bControl)
3105 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3106 POINT ptCaretPos;
3107 ptCaretPos.y = GetLineCount()-1;
3108 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3109 SetCaretAndGoalPosition(ptCaretPos);
3110 if (bShift)
3111 AdjustSelection(MOVERIGHT);
3112 else
3113 ClearSelection();
3115 else
3117 POINT ptCaretPos = GetCaretPosition();
3118 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3119 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3121 ptCaretPos.x--;
3123 SetCaretAndGoalPosition(ptCaretPos);
3124 OnCaretMove(MOVERIGHT, bShift);
3127 break;
3128 case VK_BACK:
3129 if (IsWritable())
3131 if (! HasTextSelection())
3133 POINT ptCaretPos = GetCaretPosition();
3134 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3135 break;
3136 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3137 if (bControl)
3138 MoveCaretWordLeft();
3139 else
3141 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3145 m_ptSelectionViewPosStart = GetCaretViewPosition();
3147 RemoveSelectedText();
3149 break;
3150 case VK_DELETE:
3151 if (IsWritable())
3153 if (! HasTextSelection())
3155 if (bControl)
3157 m_ptSelectionViewPosStart = GetCaretViewPosition();
3158 MoveCaretWordRight();
3159 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3161 else
3163 if (! MoveCaretRight())
3164 break;
3165 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3166 MoveCaretLeft();
3167 m_ptSelectionViewPosStart = GetCaretViewPosition();
3170 RemoveSelectedText();
3172 break;
3173 case VK_INSERT:
3174 m_bInsertMode = !m_bInsertMode;
3175 UpdateCaret();
3176 break;
3177 case 'M':
3178 if (bControl && m_pwndRight)
3180 int nFirstViewLine = 0;
3181 int nLastViewLine = 0;
3182 if (GetViewSelection(nFirstViewLine, nLastViewLine))
3183 m_pwndRight->MarkBlock(!m_pwndRight->GetViewMarked(nFirstViewLine));
3185 break;
3187 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3190 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3192 const int nClickedLine = GetButtonEventLineIndex(point);
3193 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3195 POINT ptCaretPos;
3196 ptCaretPos.y = nClickedLine;
3197 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3198 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3199 SetCaretAndGoalPosition(ptCaretPos);
3201 if (nFlags & MK_SHIFT)
3202 AdjustSelection(MOVERIGHT);
3203 else
3205 ClearSelection();
3206 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3207 if (point.x < GetMarginWidth())
3209 // select the whole line
3210 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3211 m_ptSelectionViewPosStart.x = 0;
3212 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3216 UpdateViewsCaretPosition();
3217 Invalidate();
3220 CView::OnLButtonDown(nFlags, point);
3223 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3225 if (zChar == ' ' || zChar == '\t' )
3227 return CHG_WHITESPACE;
3229 if (zChar < 0x20)
3231 return CHG_CONTROL;
3233 if (m_sWordSeparators.Find(zChar) >= 0)
3235 return CHG_WORDSEPARATOR;
3237 return CHG_WORDLETTER;
3240 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3242 if (m_pViewData == 0) {
3243 CView::OnLButtonDblClk(nFlags, point);
3244 return;
3247 const int nClickedLine = GetButtonEventLineIndex(point);
3248 if ( nClickedLine < 0)
3249 return;
3250 int nViewLine = GetViewLineForScreen(nClickedLine);
3251 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3253 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3255 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3257 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3258 int screenLine = FindViewLineNumber(movedindex);
3259 int nTop = screenLine - GetScreenLines()/2;
3260 if (nTop < 0)
3261 nTop = 0;
3262 ScrollAllToLine(nTop);
3263 // find and select the whole moved block
3264 int startSel = movedindex;
3265 int endSel = movedindex;
3266 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3267 startSel--;
3268 startSel++;
3269 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3270 endSel++;
3271 endSel--;
3272 m_pOtherView->SetupSelection(startSel, endSel);
3273 return CView::OnLButtonDblClk(nFlags, point);
3277 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3279 // a double click on a marker expands the hidden text
3280 int i = nViewLine;
3281 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3283 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3284 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3285 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3286 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3287 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3288 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3289 i++;
3291 BuildAllScreen2ViewVector();
3292 if (m_pwndLeft)
3293 m_pwndLeft->Invalidate();
3294 if (m_pwndRight)
3295 m_pwndRight->Invalidate();
3296 if (m_pwndBottom)
3297 m_pwndBottom->Invalidate();
3299 else
3301 POINT ptCaretPos;
3302 ptCaretPos.y = nClickedLine;
3303 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3304 SetCaretPosition(ptCaretPos);
3305 ClearSelection();
3307 POINT ptViewCarret = GetCaretViewPosition();
3308 nViewLine = ptViewCarret.y;
3309 if (nViewLine >= GetViewCount())
3310 return;
3311 const CString &sLine = GetViewLine(nViewLine);
3312 int nLineLength = sLine.GetLength();
3313 int nBasePos = ptViewCarret.x;
3314 // get target char group
3315 ECharGroup eLeft = CHG_UNKNOWN;
3316 if (nBasePos > 0)
3318 eLeft = GetCharGroup(sLine[nBasePos-1]);
3320 ECharGroup eRight = CHG_UNKNOWN;
3321 if (nBasePos < nLineLength)
3323 eRight = GetCharGroup(sLine[nBasePos]);
3325 ECharGroup eTarget = max(eRight, eLeft);
3326 // find left margin
3327 int nLeft = nBasePos;
3328 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3330 nLeft--;
3332 // get right margin
3333 int nRight = nBasePos;
3334 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3336 nRight++;
3338 // set selection
3339 m_ptSelectionViewPosStart.x = nLeft;
3340 m_ptSelectionViewPosStart.y = nViewLine;
3341 m_ptSelectionViewPosEnd.x = nRight;
3342 m_ptSelectionViewPosEnd.y = nViewLine;
3343 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3344 SetupAllViewSelection(nViewLine, nViewLine);
3345 // set caret
3346 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3347 UpdateViewsCaretPosition();
3348 UpdateGoalPos();
3350 // set mark word
3351 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3352 int nMarkWidth = max(nRight - nLeft, 0);
3353 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3354 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3356 m_sMarkedWord.Empty();
3359 if (m_pwndLeft)
3360 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3361 if (m_pwndRight)
3362 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3363 if (m_pwndBottom)
3364 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3366 Invalidate();
3367 if (m_pwndLocator)
3368 m_pwndLocator->Invalidate();
3371 CView::OnLButtonDblClk(nFlags, point);
3374 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3376 const int nClickedLine = GetButtonEventLineIndex(point);
3377 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3379 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3381 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath(static_cast<LPCTSTR>(m_sConvertedFilePath));
3382 if (pidl)
3384 SHOpenFolderAndSelectItems(pidl,0,0,0);
3385 CoTaskMemFree((LPVOID)pidl);
3388 return;
3390 POINT ptCaretPos;
3391 ptCaretPos.y = nClickedLine;
3392 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3393 SetCaretAndGoalPosition(ptCaretPos);
3394 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3395 if (m_pwndLeft)
3396 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3397 if (m_pwndRight)
3398 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3399 if (m_pwndBottom)
3400 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3401 ClearSelection();
3402 m_ptSelectionViewPosStart.x = 0;
3403 m_ptSelectionViewPosStart.y = nClickedLine;
3404 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3405 m_ptSelectionViewPosEnd.y = nClickedLine;
3406 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3407 UpdateViewsCaretPosition();
3408 Invalidate();
3409 if (m_pwndLocator)
3410 m_pwndLocator->Invalidate();
3413 void CBaseView::OnEditCopy()
3415 CString sCopyData = GetSelectedText();
3417 if (!sCopyData.IsEmpty())
3419 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3423 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3425 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3427 --m_pMainFrame->m_nMoveMovesToIgnore;
3428 CView::OnMouseMove(nFlags, point);
3429 return;
3431 int nMouseLine = GetButtonEventLineIndex(point);
3432 if (nMouseLine < -1)
3433 nMouseLine = -1;
3434 m_mouseInMargin = point.x < GetMarginWidth();
3436 ShowDiffLines(nMouseLine);
3438 KillTimer(IDT_SCROLLTIMER);
3439 if (nFlags & MK_LBUTTON)
3441 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3442 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3443 if (saveMouseLine < 0)
3444 return;
3445 int col = CalcColFromPoint(point.x, saveMouseLine);
3446 int charIndex = CalculateCharIndex(saveMouseLine, col);
3447 if (HasSelection() &&
3448 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3450 POINT ptCaretPos = {charIndex, nMouseLine};
3451 SetCaretAndGoalPosition(ptCaretPos);
3452 AdjustSelection(MOVERIGHT);
3453 Invalidate();
3454 UpdateWindow();
3456 if (nMouseLine < m_nTopLine)
3458 ScrollAllToLine(m_nTopLine-1, TRUE);
3459 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3461 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3463 ScrollAllToLine(m_nTopLine+1, TRUE);
3464 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3466 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3468 ScrollAllSide(-1);
3469 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3471 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3473 ScrollAllSide(1);
3474 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3476 SetCapture();
3480 CView::OnMouseMove(nFlags, point);
3483 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3485 ShowDiffLines(-1);
3486 ReleaseCapture();
3487 KillTimer(IDT_SCROLLTIMER);
3489 __super::OnLButtonUp(nFlags, point);
3492 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3494 if (nIDEvent == IDT_SCROLLTIMER)
3496 POINT point;
3497 GetCursorPos(&point);
3498 ScreenToClient(&point);
3499 int nMouseLine = GetButtonEventLineIndex(point);
3500 if (nMouseLine < -1)
3502 nMouseLine = -1;
3504 if (GetKeyState(VK_LBUTTON)&0x8000)
3506 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3507 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3508 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3509 if (nMouseLine < m_nTopLine)
3511 ScrollAllToLine(m_nTopLine-1, TRUE);
3512 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3514 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3516 ScrollAllToLine(m_nTopLine+1, TRUE);
3517 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3519 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3521 ScrollAllSide(-1);
3522 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3524 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3526 ScrollAllSide(1);
3527 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3533 CView::OnTimer(nIDEvent);
3536 void CBaseView::ShowDiffLines(int nLine)
3538 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3540 m_pwndLineDiffBar->ShowLines(nLine);
3541 nLine = -1;
3542 m_nMouseLine = nLine;
3543 return;
3546 if ((!m_pwndRight)||(!m_pwndLeft))
3547 return;
3548 if(m_pMainFrame->m_bOneWay)
3549 return;
3551 nLine = (nLine > m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3552 nLine = (nLine > m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3554 if (nLine < 0)
3555 return;
3557 if (nLine != m_nMouseLine)
3559 if (nLine >= GetLineCount())
3560 nLine = -1;
3561 m_nMouseLine = nLine;
3562 m_pwndLineDiffBar->ShowLines(nLine);
3564 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3567 const viewdata& CBaseView::GetEmptyLineData()
3569 static const viewdata emptyLine(L"", DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3570 return emptyLine;
3573 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3575 for (int i = 0; i < nCount; i++)
3577 InsertViewData(nFirstView, GetEmptyLineData());
3582 void CBaseView::UpdateCaret()
3584 POINT ptCaretPos = GetCaretPosition();
3585 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3586 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3587 SetCaretPosition(ptCaretPos);
3589 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3591 if (m_bFocused &&
3592 ptCaretPos.y >= m_nTopLine &&
3593 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3594 nCaretOffset >= m_nOffsetChar &&
3595 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3597 POINT pt1 = TextToClient(ptCaretPos);
3598 if (m_bInsertMode)
3599 CreateSolidCaret(2, GetLineHeight());
3600 else
3602 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3603 POINT pt2 = TextToClient(pt);
3604 int width = max(GetCharWidth(), static_cast<int>(pt2.x - pt1.x));
3605 CreateSolidCaret(width, GetLineHeight());
3607 SetCaretPos(pt1);
3608 ShowCaret();
3610 else
3612 HideCaret();
3616 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3618 POINT ptViewPos;
3619 ptViewPos.x = pt.x;
3621 int nSubLine = GetSubLineOffset(pt.y);
3622 if (nSubLine > 0)
3624 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3626 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3630 ptViewPos.y = GetViewLineForScreen(pt.y);
3631 return ptViewPos;
3634 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3636 POINT ptPos;
3637 int nViewLineLenLeft = GetViewLineLength(pt.y);
3638 ptPos.x = min(static_cast<LONG>(nViewLineLenLeft), pt.x);
3639 ptPos.y = FindScreenLineForViewLine(pt.y);
3640 if (GetViewLineForScreen(ptPos.y) != pt.y )
3642 ptPos.x = 0;
3644 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3646 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3647 while (nSubLineLength < ptPos.x)
3649 ptPos.x -= nSubLineLength;
3650 nViewLineLenLeft -= nSubLineLength;
3651 ptPos.y++;
3652 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3654 // last pos of non last sub-line go to start of next screen line
3655 // Note: while this works correctly, it's not what a user might expect:
3656 // cursor-right when the caret is before the last char of a wrapped line
3657 // now moves the caret to the next line. But users expect the caret to
3658 // move to the right of the last char instead, and with another cursor-right
3659 // keystroke to move the caret to the next line.
3660 // Basically, this would require to handle two caret positions for the same
3661 // logical position in the line string (one on the last position of the first line,
3662 // one on the first position of the new line. For non-wrapped lines this works
3663 // because there's an 'invisible' newline char at the end of the first line.
3664 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3666 ptPos.x = 0;
3667 ptPos.y++;
3671 return ptPos;
3675 void CBaseView::EnsureCaretVisible()
3677 POINT ptCaretPos = GetCaretPosition();
3678 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3680 if (ptCaretPos.y < m_nTopLine)
3681 ScrollAllToLine(ptCaretPos.y);
3682 int screnLines = GetScreenLines();
3683 if (screnLines)
3685 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3686 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3687 if (nCaretOffset < m_nOffsetChar)
3688 ScrollAllToChar(nCaretOffset);
3689 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3690 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3694 int CBaseView::CalculateActualOffset(const POINT& point)
3696 int nLineIndex = point.y;
3697 int nCharIndex = point.x;
3698 ASSERT(nCharIndex >= 0);
3699 CString sLine = GetLineChars(nLineIndex);
3700 int nLineLength = sLine.GetLength();
3701 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3704 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3706 int nLength = GetLineLength(nLineIndex);
3707 int nSubLine = GetSubLineOffset(nLineIndex);
3708 if (nSubLine>=0)
3710 int nViewLine = GetViewLineForScreen(nLineIndex);
3711 if (nViewLine >= 0 && nViewLine < static_cast<int>(m_ScreenedViewLine.size()))
3713 int nMultilineCount = CountMultiLines(nViewLine);
3714 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3716 nLength--;
3720 CString Line = GetLineChars(nLineIndex);
3721 int nIndex = 0;
3722 int nOffset = 0;
3723 int nTabSize = GetTabSize();
3724 while (nOffset < nActualOffset && nIndex < nLength)
3726 if (Line.GetAt(nIndex) == L'\t')
3727 nOffset += (nTabSize - nOffset % nTabSize);
3728 else
3729 ++nOffset;
3730 ++nIndex;
3732 return nIndex;
3736 * @param xpos X coordinate in CBaseView
3737 * @param lineIndex logical line index (e.g. wrap/collapse)
3739 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3741 int xpos2;
3742 CDC *pDC = GetDC();
3743 if (pDC)
3745 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3746 int fit = text.GetLength();
3747 auto posBuffer = std::make_unique<int[]>(fit);
3748 pDC->SelectObject(GetFont()); // is this right font ?
3749 SIZE size;
3750 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3751 ReleaseDC(pDC);
3752 int lower = -1, upper = fit - 1;
3753 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3756 int middle = (upper + lower + 1) / 2;
3757 int width = posBuffer[middle];
3758 if (xcheck < width)
3759 upper = middle - 1;
3760 else
3761 lower = middle;
3762 } while (lower < upper);
3763 lower++;
3764 xpos2 = lower;
3765 if (lower < fit - 1)
3767 int charWidth = posBuffer[lower] - (lower > 0 ? posBuffer[lower - 1] : 0);
3768 if (posBuffer[lower] - xcheck <= charWidth / 2)
3769 xpos2++;
3772 else
3774 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3775 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3776 xpos2++;
3778 return xpos2;
3781 POINT CBaseView::TextToClient(const POINT& point)
3783 POINT pt;
3784 int nOffsetScreenLine = max(0, static_cast<int>(point.y - m_nTopLine));
3785 pt.y = nOffsetScreenLine * GetLineHeight();
3786 pt.x = CalculateActualOffset(point);
3788 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3789 CDC * pDC = GetDC();
3790 if (pDC)
3792 pDC->SelectObject(GetFont()); // is this right font ?
3793 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3794 CString sLine = GetLineChars(nScreenLine);
3795 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3796 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3797 ReleaseDC(pDC);
3798 } else {
3799 nLeft += pt.x * GetCharWidth();
3802 pt.x = nLeft;
3803 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3804 return pt;
3807 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3809 CView::OnChar(nChar, nRepCnt, nFlags);
3811 bool bControl = !!(GetKeyState(VK_CONTROL) & 0x8000);
3812 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3813 bool bSkipSelectionClear = false;
3815 if (IsReadonly())
3816 return;
3818 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3819 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3821 return;
3824 if (!m_pViewData) // no data - nothing to do
3825 return;
3827 if (nChar == VK_F16)
3829 // generated by a ctrl+backspace - ignore.
3831 else if (nChar==VK_TAB && HasTextLineSelection())
3833 // change indentation for selected lines
3834 if (bShift)
3836 RemoveIndentationForSelectedBlock();
3838 else
3840 AddIndentationForSelectedBlock();
3842 bSkipSelectionClear = true;
3844 else if ((nChar > 31)||(nChar == VK_TAB))
3846 ResetUndoStep();
3847 RemoveSelectedText();
3848 POINT ptCaretViewPos = GetCaretViewPosition();
3849 int nViewLine = ptCaretViewPos.y;
3850 if ((nViewLine==0)&&(GetViewCount()==0))
3851 OnChar(VK_RETURN, 0, 0);
3852 int charCount = 1;
3853 viewdata lineData = GetViewData(nViewLine);
3854 if (nChar == VK_TAB)
3856 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3857 if (indentChars > 0)
3859 lineData.sLine.Insert(ptCaretViewPos.x, CString(L' ', indentChars));
3860 charCount = indentChars;
3862 else
3863 lineData.sLine.Insert(ptCaretViewPos.x, L'\t');
3865 else
3867 if (m_bInsertMode)
3868 lineData.sLine.Insert(ptCaretViewPos.x, static_cast<wchar_t>(nChar));
3869 else
3871 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3872 lineData.sLine.SetAt(ptCaretViewPos.x, static_cast<wchar_t>(nChar));
3873 else
3874 lineData.sLine.Insert(ptCaretViewPos.x, static_cast<wchar_t>(nChar));
3877 if (IsStateEmpty(lineData.state) || IsStateConflicted(lineData.state) || lineData.state == DIFFSTATE_IDENTICALREMOVED)
3879 // if not last line set EOL
3880 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3882 if (!IsViewLineEmpty(nCheckViewLine))
3884 lineData.ending = m_lineendings;
3885 break;
3888 // make sure previous (non empty) line have EOL set
3889 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3891 if (!IsViewLineEmpty(nCheckViewLine) && GetViewState(nCheckViewLine) != DIFFSTATE_IDENTICALREMOVED)
3893 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3895 SetViewLineEnding(nCheckViewLine, m_lineendings);
3897 break;
3901 lineData.state = DIFFSTATE_EDITED;
3902 bool bNeedRenumber = false;
3903 if (lineData.linenumber == -1)
3905 lineData.linenumber = 0;
3906 bNeedRenumber = true;
3908 SetViewData(nViewLine, lineData);
3909 SetModified();
3910 SaveUndoStep();
3911 BuildAllScreen2ViewVector(nViewLine);
3912 if (bNeedRenumber)
3914 UpdateViewLineNumbers();
3916 for (int i = 0; i < charCount; ++i)
3917 MoveCaretRight();
3918 UpdateGoalPos();
3920 else if (nChar == 10)
3922 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3923 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3924 EOL newEOL = EOL_CRLF;
3925 switch (eol)
3927 case EOL_CRLF:
3928 newEOL = EOL_CR;
3929 break;
3930 case EOL_CR:
3931 newEOL = EOL_LF;
3932 break;
3933 case EOL_LF:
3934 newEOL = EOL_CRLF;
3935 break;
3937 if (eol==EOL_NOENDING || eol==newEOL)
3938 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3939 // to add EOL on newly edited empty line hit enter
3940 // don't store into UNDO if no change happened
3941 // and don't mark file as modified
3942 return;
3943 AddUndoViewLine(nViewLine);
3944 m_pViewData->SetLineEnding(nViewLine, newEOL);
3945 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3946 UpdateGoalPos();
3948 else if ((nChar == VK_RETURN) && !bControl)
3950 // insert a new, fresh and empty line below the cursor
3951 RemoveSelectedText();
3953 CUndo::GetInstance().BeginGrouping();
3955 POINT ptCaretViewPos = GetCaretViewPosition();
3956 int nViewLine = ptCaretViewPos.y;
3957 int nLeft = ptCaretViewPos.x;
3958 CString sLine = GetViewLineChars(nViewLine);
3959 CString sLineLeft = sLine.Left(nLeft);
3960 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3961 EOL eOriginalEnding = EOL_AUTOLINE;
3962 if (m_pViewData->GetCount() > nViewLine)
3963 eOriginalEnding = GetViewLineEnding(nViewLine);
3965 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3967 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3968 SetViewData(nViewLine, newFirstLine);
3971 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3972 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3973 InsertViewData(nInsertLine, newLastLine);
3974 SetModified();
3975 SaveUndoStep();
3977 // adds new line everywhere except me
3978 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3980 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3982 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3984 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3986 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3988 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3990 SaveUndoStep();
3992 UpdateViewLineNumbers();
3993 SaveUndoStep();
3994 CUndo::GetInstance().EndGrouping();
3996 BuildAllScreen2ViewVector();
3997 // move the cursor to the new line
3998 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3999 SetCaretAndGoalViewPosition(ptCaretViewPos);
4001 else
4002 return; // Unknown control character -- ignore it.
4003 if (!bSkipSelectionClear)
4004 ClearSelection();
4005 EnsureCaretVisible();
4006 UpdateCaret();
4007 Invalidate(FALSE);
4010 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
4012 ResetUndoStep();
4013 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
4014 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
4015 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
4016 SetModified();
4017 SaveUndoStep();
4018 RecalcAllVertScrollBars();
4019 Invalidate(FALSE);
4022 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
4024 if (!m_pViewData)
4025 return;
4026 int viewLine = nViewLineIndex;
4027 EOL ending = m_pViewData->GetLineEnding(viewLine);
4028 if (ending == EOL_NOENDING)
4030 ending = m_lineendings;
4032 viewdata newLine(L"", DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
4033 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
4035 CString sPartLine = GetViewLineChars(nViewLineIndex);
4036 int nPosx = GetCaretPosition().x; // should be view pos ?
4037 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
4038 sPartLine = sPartLine.Mid(nPosx);
4039 newLine.sLine = sPartLine;
4041 m_pViewData->InsertData(viewLine+1, newLine);
4042 BuildAllScreen2ViewVector();
4045 void CBaseView::RemoveSelectedText()
4047 if (!m_pViewData)
4048 return;
4049 if (!HasTextSelection())
4050 return;
4052 // fix selection if starts or ends on empty line
4053 SetCaretViewPosition(m_ptSelectionViewPosEnd);
4054 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4057 m_ptSelectionViewPosEnd = GetCaretViewPosition();
4058 SetCaretViewPosition(m_ptSelectionViewPosStart);
4059 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4062 m_ptSelectionViewPosStart = GetCaretViewPosition();
4063 if (!HasTextSelection())
4065 ClearSelection();
4066 return;
4069 // We want to undo the insertion in a single step.
4070 ResetUndoStep();
4071 CUndo::GetInstance().BeginGrouping();
4073 // combine first and last line
4074 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
4075 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
4076 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
4077 oFirstLine.ending = oLastLine.ending;
4078 oFirstLine.state = DIFFSTATE_EDITED;
4079 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4081 // clean up middle lines if any
4082 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4084 viewdata oEmptyLine = GetEmptyLineData();
4085 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4087 SetViewData(nViewLine, oEmptyLine);
4089 SaveUndoStep();
4091 if (CleanEmptyLines())
4093 BuildAllScreen2ViewVector(); // schedule full rebuild
4095 SaveUndoStep();
4096 UpdateViewLineNumbers();
4099 SetModified(); //TODO set modified only if real data was changed
4100 SaveUndoStep();
4101 CUndo::GetInstance().EndGrouping();
4103 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4104 SetCaretViewPosition(m_ptSelectionViewPosStart);
4105 UpdateGoalPos();
4106 ClearSelection();
4107 UpdateCaret();
4108 EnsureCaretVisible();
4109 Invalidate(FALSE);
4112 void CBaseView::PasteText()
4114 if (!OpenClipboard())
4115 return;
4117 CString sClipboardText;
4118 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4119 if (hglb)
4121 LPCSTR lpstr = static_cast<LPCSTR>(GlobalLock(hglb));
4122 sClipboardText = CString(lpstr);
4123 GlobalUnlock(hglb);
4125 hglb = GetClipboardData(CF_UNICODETEXT);
4126 if (hglb)
4128 LPCTSTR lpstr = static_cast<LPCTSTR>(GlobalLock(hglb));
4129 sClipboardText = lpstr;
4130 GlobalUnlock(hglb);
4132 CloseClipboard();
4134 if (sClipboardText.IsEmpty())
4135 return;
4137 sClipboardText.Replace(L"\r\n", L"\r");
4138 sClipboardText.Replace('\n', '\r');
4140 InsertText(sClipboardText);
4143 void CBaseView::OnCaretDown()
4145 POINT ptCaretPos = GetCaretPosition();
4146 int nLine = ptCaretPos.y;
4147 int nNextLine = nLine + 1;
4148 if (nNextLine >= GetLineCount()) // already at last line
4150 return;
4153 POINT ptCaretViewPos = GetCaretViewPosition();
4154 int nViewLine = ptCaretViewPos.y;
4155 int nNextViewLine = GetViewLineForScreen(nNextLine);
4156 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4158 // find next suitable screen line
4159 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4161 nNextLine++;
4162 if (nNextLine >= GetLineCount())
4164 return;
4166 nNextViewLine = GetViewLineForScreen(nNextLine);
4169 ptCaretPos.y = nNextLine;
4170 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4171 SetCaretPosition(ptCaretPos);
4172 OnCaretMove(MOVELEFT);
4173 ShowDiffLines(ptCaretPos.y);
4176 bool CBaseView::MoveCaretLeft()
4178 POINT ptCaretViewPos = GetCaretViewPosition();
4180 //int nViewLine = ptCaretViewPos.y;
4181 if (ptCaretViewPos.x == 0)
4183 int nPrevLine = GetCaretPosition().y;
4184 int nPrevViewLine;
4185 do {
4186 nPrevLine--;
4187 if (nPrevLine < 0)
4189 return false;
4191 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4192 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4193 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4194 ShowDiffLines(nPrevLine);
4196 else
4197 --ptCaretViewPos.x;
4199 SetCaretAndGoalViewPosition(ptCaretViewPos);
4200 return true;
4203 bool CBaseView::MoveCaretRight()
4205 POINT ptCaretViewPos = GetCaretViewPosition();
4207 int nViewLine = ptCaretViewPos.y;
4208 int nViewLineLen = GetViewLineLength(nViewLine);
4209 if (ptCaretViewPos.x >= nViewLineLen)
4211 int nNextLine = GetCaretPosition().y;
4212 int nNextViewLine;
4213 do {
4214 nNextLine++;
4215 if (nNextLine >= GetLineCount())
4217 return false;
4219 nNextViewLine = GetViewLineForScreen(nNextLine);
4220 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4221 ptCaretViewPos.y = nNextViewLine;
4222 ptCaretViewPos.x = 0;
4223 ShowDiffLines(nNextLine);
4225 else
4226 ++ptCaretViewPos.x;
4228 SetCaretAndGoalViewPosition(ptCaretViewPos);
4229 return true;
4232 void CBaseView::UpdateGoalPos()
4234 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4237 void CBaseView::OnCaretLeft()
4239 MoveCaretLeft();
4240 OnCaretMove(MOVELEFT);
4243 void CBaseView::OnCaretRight()
4245 MoveCaretRight();
4246 OnCaretMove(MOVERIGHT);
4249 void CBaseView::OnCaretUp()
4251 POINT ptCaretPos = GetCaretPosition();
4252 int nLine = ptCaretPos.y;
4253 if (nLine <= 0) // already at first line
4255 return;
4257 int nPrevLine = nLine - 1;
4259 POINT ptCaretViewPos = GetCaretViewPosition();
4260 int nViewLine = ptCaretViewPos.y;
4261 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4262 if (nPrevViewLine != nViewLine) // not on same view line
4264 // find previous suitable screen line
4265 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4267 if (nPrevLine <= 0)
4269 return;
4271 nPrevLine--;
4272 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4275 ptCaretPos.y = nPrevLine;
4276 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4277 SetCaretPosition(ptCaretPos);
4278 OnCaretMove(MOVELEFT);
4279 ShowDiffLines(ptCaretPos.y);
4282 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4284 switch (GetCharGroup(ch))
4286 case CHG_CONTROL:
4287 case CHG_WHITESPACE:
4288 case CHG_WORDSEPARATOR:
4289 return true;
4291 return false;
4294 bool CBaseView::IsCaretAtWordBoundary()
4296 POINT ptViewCaret = GetCaretViewPosition();
4297 CString line = GetViewLineChars(ptViewCaret.y);
4298 if (line.IsEmpty())
4299 return false; // no boundary at the empty lines
4300 if (ptViewCaret.x == 0)
4301 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4302 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4303 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4304 return
4305 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4306 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4309 void CBaseView::UpdateViewsCaretPosition()
4311 POINT ptCaretPos = GetCaretPosition();
4312 if (m_pwndBottom && m_pwndBottom!=this)
4313 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4314 if (m_pwndLeft && m_pwndLeft!=this)
4315 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4316 if (m_pwndRight && m_pwndRight!=this)
4317 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4320 void CBaseView::OnCaretWordleft()
4322 MoveCaretWordLeft();
4323 OnCaretMove(MOVELEFT);
4326 void CBaseView::OnCaretWordright()
4328 MoveCaretWordRight();
4329 OnCaretMove(MOVERIGHT);
4332 void CBaseView::MoveCaretWordLeft()
4334 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4339 void CBaseView::MoveCaretWordRight()
4341 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4346 void CBaseView::ClearCurrentSelection()
4348 m_ptSelectionViewPosStart = GetCaretViewPosition();
4349 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4350 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4351 m_nSelViewBlockStart = -1;
4352 m_nSelViewBlockEnd = -1;
4353 Invalidate(FALSE);
4356 void CBaseView::ClearSelection()
4358 if (m_pwndLeft)
4359 m_pwndLeft->ClearCurrentSelection();
4360 if (m_pwndRight)
4361 m_pwndRight->ClearCurrentSelection();
4362 if (m_pwndBottom)
4363 m_pwndBottom->ClearCurrentSelection();
4366 void CBaseView::AdjustSelection(bool bMoveLeft)
4368 POINT ptCaretViewPos = GetCaretViewPosition();
4369 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4371 // select all have been used recently update origin
4372 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4374 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4375 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4377 m_ptSelectionViewPosStart = ptCaretViewPos;
4378 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4380 else
4382 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4383 m_ptSelectionViewPosEnd = ptCaretViewPos;
4386 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4388 Invalidate(FALSE);
4391 void CBaseView::OnEditCut()
4393 if (IsWritable())
4395 OnEditCopy();
4396 RemoveSelectedText();
4400 void CBaseView::OnEditPaste()
4402 if (IsWritable())
4404 CUndo::GetInstance().BeginGrouping();
4405 RemoveSelectedText();
4406 PasteText();
4407 CUndo::GetInstance().EndGrouping();
4411 void CBaseView::DeleteFonts()
4413 for (int i=0; i<fontsCount; i++)
4415 if (m_apFonts[i])
4417 m_apFonts[i]->DeleteObject();
4418 delete m_apFonts[i];
4419 m_apFonts[i] = nullptr;
4424 void CBaseView::OnCaretMove(bool bMoveLeft)
4426 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4427 OnCaretMove(bMoveLeft, bShift);
4430 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4432 if(isShiftPressed)
4433 AdjustSelection(bMoveLeft);
4434 else
4435 ClearSelection();
4436 EnsureCaretVisible();
4437 UpdateCaret();
4440 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4442 AddCutCopyAndPaste(popup);
4445 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4447 popup.AppendMenu(MF_SEPARATOR, NULL);
4448 CString temp;
4449 temp.LoadString(IDS_EDIT_COPY);
4450 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4451 if (IsWritable())
4453 temp.LoadString(IDS_EDIT_CUT);
4454 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4455 temp.LoadString(IDS_EDIT_PASTE);
4456 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4457 popup.AppendMenu(MF_SEPARATOR, NULL);
4461 void CBaseView::CompensateForKeyboard(CPoint& point)
4463 // if the context menu is invoked through the keyboard, we have to use
4464 // a calculated position on where to anchor the menu on
4465 if (ArePointsSame(point, SetupPoint(-1, -1)))
4467 CRect rect;
4468 GetWindowRect(&rect);
4469 point = rect.CenterPoint();
4473 void CBaseView::ReleaseBitmap()
4475 if (m_pCacheBitmap)
4477 m_pCacheBitmap->DeleteObject();
4478 delete m_pCacheBitmap;
4479 m_pCacheBitmap = nullptr;
4483 void CBaseView::BuildMarkedWordArray()
4485 int lineCount = GetLineCount();
4486 m_arMarkedWordLines.clear();
4487 m_arMarkedWordLines.reserve(lineCount);
4488 bool bDoit = !m_sMarkedWord.IsEmpty();
4489 for (int i = 0; i < lineCount; ++i)
4491 if (bDoit)
4493 CString line = GetLineChars(i);
4495 if (!line.IsEmpty())
4497 int found = 0;
4498 int nMarkStart = -1;
4499 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4501 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4502 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4503 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4504 if (eLeft != eStart)
4506 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4507 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4508 if (eRight != eEnd)
4510 found = 1;
4511 break;
4515 m_arMarkedWordLines.push_back(found);
4517 else
4518 m_arMarkedWordLines.push_back(0);
4520 else
4521 m_arMarkedWordLines.push_back(0);
4525 void CBaseView::BuildFindStringArray()
4527 int lineCount = GetLineCount();
4528 m_arFindStringLines.clear();
4529 m_arFindStringLines.reserve(lineCount);
4530 bool bDoit = !m_sFindText.IsEmpty();
4531 int s = 0;
4532 int e = 0;
4533 for (int i = 0; i < lineCount; ++i)
4535 if (bDoit)
4537 CString line = GetLineChars(i);
4539 if (!line.IsEmpty())
4541 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4543 case DIFFSTATE_EMPTY:
4544 m_arFindStringLines.push_back(0);
4545 break;
4546 case DIFFSTATE_UNKNOWN:
4547 case DIFFSTATE_NORMAL:
4548 case DIFFSTATE_FILTEREDDIFF:
4549 if (m_bLimitToDiff)
4551 m_arFindStringLines.push_back(0);
4552 break;
4554 case DIFFSTATE_REMOVED:
4555 case DIFFSTATE_REMOVEDWHITESPACE:
4556 case DIFFSTATE_ADDED:
4557 case DIFFSTATE_ADDEDWHITESPACE:
4558 case DIFFSTATE_WHITESPACE:
4559 case DIFFSTATE_WHITESPACE_DIFF:
4560 case DIFFSTATE_CONFLICTED:
4561 case DIFFSTATE_CONFLICTED_IGNORED:
4562 case DIFFSTATE_CONFLICTADDED:
4563 case DIFFSTATE_CONFLICTEMPTY:
4564 case DIFFSTATE_CONFLICTRESOLVED:
4565 case DIFFSTATE_IDENTICALREMOVED:
4566 case DIFFSTATE_IDENTICALADDED:
4567 case DIFFSTATE_THEIRSREMOVED:
4568 case DIFFSTATE_THEIRSADDED:
4569 case DIFFSTATE_YOURSREMOVED:
4570 case DIFFSTATE_YOURSADDED:
4571 case DIFFSTATE_EDITED:
4573 if (!m_bMatchCase)
4574 line = line.MakeLower();
4575 s = 0;
4576 e = 0;
4577 int match = 0;
4578 while (StringFound(line, SearchNext, s, e))
4580 match++;
4581 s = e;
4582 e = 0;
4584 m_arFindStringLines.push_back(match);
4585 break;
4587 default:
4588 m_arFindStringLines.push_back(0);
4591 else
4592 m_arFindStringLines.push_back(0);
4594 else
4595 m_arFindStringLines.push_back(0);
4597 UpdateLocator();
4600 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4602 if (!m_bShowInlineDiff)
4603 return false;
4604 if (m_pwndBottom && !(m_pwndBottom->IsHidden()))
4605 return false;
4607 if (!m_pViewData || m_pViewData->GetCount() <= nViewLine)
4608 return false;
4609 const CString &sLine = m_pViewData->GetLine(nViewLine);
4610 if (sLine.IsEmpty())
4611 return false;
4613 CheckOtherView();
4614 if (!m_pOtherViewData)
4615 return false;
4617 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4618 if (sDiffLine.IsEmpty())
4619 return false;
4621 svn_diff_t* diff = nullptr;
4622 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4623 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4624 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4625 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4626 return false;
4628 size_t lineoffset = 0;
4629 size_t position = 0;
4630 while (diff)
4632 if (this == m_pwndRight)
4634 apr_off_t nTmp = diff->modified_length;
4635 diff->modified_length = diff->original_length;
4636 diff->original_length = nTmp;
4638 nTmp = diff->modified_start;
4639 diff->modified_start = diff->original_start;
4640 diff->original_start = nTmp;
4642 apr_off_t len = diff->original_length;
4643 size_t oldpos = position;
4645 for (apr_off_t i = 0; i < len; ++i)
4647 position += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4648 lineoffset++;
4651 if (diff->type == svn_diff__type_diff_modified)
4653 inlineDiffPos p;
4654 p.start = oldpos;
4655 p.end = position;
4656 positions.push_back(p);
4659 diff = diff->next;
4662 return !positions.empty();
4665 void CBaseView::OnNavigateNextinlinediff()
4667 int nX;
4668 if (GetNextInlineDiff(nX))
4670 POINT ptCaretViewPos = GetCaretViewPosition();
4671 ptCaretViewPos.x = nX;
4672 SetCaretAndGoalViewPosition(ptCaretViewPos);
4673 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4674 EnsureCaretVisible();
4678 void CBaseView::OnNavigatePrevinlinediff()
4680 int nX;
4681 if (GetPrevInlineDiff(nX))
4683 POINT ptCaretViewPos = GetCaretViewPosition();
4684 ptCaretViewPos.x = nX;
4685 SetCaretAndGoalViewPosition(ptCaretViewPos);
4686 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4687 EnsureCaretVisible();
4691 bool CBaseView::HasNextInlineDiff()
4693 int nPos;
4694 return GetNextInlineDiff(nPos);
4697 bool CBaseView::GetNextInlineDiff(int & nPos)
4699 POINT ptCaretViewPos = GetCaretViewPosition();
4700 std::vector<inlineDiffPos> positions;
4701 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4703 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4705 if (it->start > ptCaretViewPos.x)
4707 nPos = static_cast<LONG>(it->start);
4708 return true;
4710 if (it->end > ptCaretViewPos.x)
4712 nPos = static_cast<LONG>(it->end);
4713 return true;
4717 return false;
4720 bool CBaseView::HasPrevInlineDiff()
4722 int nPos;
4723 return GetPrevInlineDiff(nPos);
4726 bool CBaseView::GetPrevInlineDiff(int & nPos)
4728 POINT ptCaretViewPos = GetCaretViewPosition();
4729 std::vector<inlineDiffPos> positions;
4730 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4732 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4734 if ( it->end < ptCaretViewPos.x)
4736 nPos = static_cast<LONG>(it->end);
4737 return true;
4739 if ( it->start < ptCaretViewPos.x)
4741 nPos = static_cast<LONG>(it->start);
4742 return true;
4746 return false;
4749 CBaseView * CBaseView::GetFirstGoodView()
4751 if (IsViewGood(m_pwndLeft))
4752 return m_pwndLeft;
4753 if (IsViewGood(m_pwndRight))
4754 return m_pwndRight;
4755 if (IsViewGood(m_pwndBottom))
4756 return m_pwndBottom;
4757 return nullptr;
4760 void CBaseView::BuildAllScreen2ViewVector()
4762 CBaseView * p_pwndView = GetFirstGoodView();
4763 if (p_pwndView)
4765 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4769 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4771 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4774 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4776 CBaseView * p_pwndView = GetFirstGoodView();
4777 if (p_pwndView)
4779 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4783 void CBaseView::UpdateViewLineNumbers()
4785 int nLineNumber = 0;
4786 int nViewLineCount = GetViewCount();
4787 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4789 int oldLine = GetViewLineNumber(nViewLine);
4790 if (oldLine >= 0)
4791 SetViewLineNumber(nViewLine, nLineNumber++);
4793 m_nDigits = 0;
4796 int CBaseView::CleanEmptyLines()
4798 int nRemovedCount = 0;
4799 int nViewLineCount = GetViewCount();
4800 bool bCheckLeft = IsViewGood(m_pwndLeft);
4801 bool bCheckRight = IsViewGood(m_pwndRight);
4802 bool bCheckBottom = IsViewGood(m_pwndBottom);
4803 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4805 bool bAllEmpty = true;
4806 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4807 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4808 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4809 if (bAllEmpty)
4811 if (bCheckLeft)
4813 m_pwndLeft->RemoveViewData(nViewLine);
4815 if (bCheckRight)
4817 m_pwndRight->RemoveViewData(nViewLine);
4819 if (bCheckBottom)
4821 m_pwndBottom->RemoveViewData(nViewLine);
4823 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4825 SaveUndoStep();
4827 nViewLineCount--;
4828 nRemovedCount++;
4829 continue;
4831 nViewLine++;
4833 return nRemovedCount;
4836 int CBaseView::FindScreenLineForViewLine( int viewLine )
4838 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4841 int CBaseView::CountMultiLines( int nViewLine )
4843 if (m_ScreenedViewLine.empty())
4844 return 0; // in case the view is completely empty
4846 ASSERT(nViewLine < static_cast<int>(m_ScreenedViewLine.size()));
4848 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4850 return static_cast<int>(m_ScreenedViewLine[nViewLine].SubLines.size());
4853 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4855 TScreenedViewLine oScreenedLine;
4856 // tokenize string
4857 int prevpos = 0;
4858 int pos = 0;
4859 while ((pos = multiline.Find('\n', pos)) >= 0)
4861 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4862 pos++;
4863 prevpos = pos;
4865 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4866 oScreenedLine.bSublinesSet = true;
4867 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4869 return CountMultiLines(nViewLine);
4872 /// prepare inline diff cache
4873 LineColors & CBaseView::GetLineColors(int nViewLine)
4875 ASSERT(nViewLine < static_cast<int>(m_ScreenedViewLine.size()));
4877 if (m_bWhitespaceInlineDiffs)
4879 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4880 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4882 else
4884 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4885 return m_ScreenedViewLine[nViewLine].lineColors;
4888 LineColors oLineColors;
4889 // set main line color
4890 COLORREF crBkgnd, crText;
4891 DiffStates diffState = m_pViewData->GetState(nViewLine);
4892 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4893 oLineColors.SetColor(0, crText, crBkgnd);
4895 do {
4896 if (!m_bShowInlineDiff)
4897 break;
4899 if (((diffState == DIFFSTATE_NORMAL) || (diffState == DIFFSTATE_FILTEREDDIFF)) && (!m_bWhitespaceInlineDiffs))
4900 break;
4902 CString sLine = GetViewLineChars(nViewLine);
4903 if (sLine.IsEmpty())
4904 break;
4905 CString sDiffLine;
4906 if (!m_pOtherView)
4908 switch (diffState)
4910 case DIFFSTATE_ADDED:
4912 if ((nViewLine > 0) && (m_pViewData->GetState(nViewLine - 1) == DIFFSTATE_REMOVED))
4913 sDiffLine = GetViewLineChars(nViewLine - 1);
4915 break;
4916 case DIFFSTATE_REMOVED:
4918 if (((nViewLine + 1) < m_pViewData->GetCount()) && (m_pViewData->GetState(nViewLine + 1) == DIFFSTATE_ADDED))
4919 sDiffLine = GetViewLineChars(nViewLine + 1);
4921 break;
4924 else
4925 sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4926 if (sDiffLine.IsEmpty())
4927 break;
4929 svn_diff_t* diff = nullptr;
4930 if (sLine.GetLength() > static_cast<int>(m_nInlineDiffMaxLineLength))
4931 break;
4932 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4933 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4934 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4935 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4936 break;
4938 int lineoffset = 0;
4939 int nTextStartOffset = 0;
4940 std::map<int, COLORREF> removedPositions;
4941 while (diff)
4943 if (this == m_pwndRight)
4945 apr_off_t nTmp = diff->modified_length;
4946 diff->modified_length = diff->original_length;
4947 diff->original_length = nTmp;
4949 nTmp = diff->modified_start;
4950 diff->modified_start = diff->original_start;
4951 diff->original_start = nTmp;
4953 apr_off_t len = diff->original_length;
4955 size_t nTextLength = 0;
4956 for (int i = 0; i < len; ++i)
4958 nTextLength += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4959 lineoffset++;
4961 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4963 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4964 if ((m_bShowInlineDiff)&&(bInlineDiff))
4966 crBkgnd = InlineViewLineDiffColor(nViewLine);
4968 else if (m_pOtherView)
4970 crBkgnd = m_ModifiedBk;
4973 if (len < diff->modified_length)
4975 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4977 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4979 nTextStartOffset += static_cast<int>(nTextLength);
4980 diff = diff->next;
4982 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4984 oLineColors.AddShotColor(it->first, it->second);
4986 } while (false); // error catch
4988 if (!m_bWhitespaceInlineDiffs)
4990 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4991 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4993 else
4995 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4996 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4999 return GetLineColors(nViewLine);
5002 void CBaseView::OnEditSelectall()
5004 if (!m_pViewData)
5005 return;
5006 int nLastViewLine = m_pViewData->GetCount()-1;
5007 if (nLastViewLine < 0)
5008 return;
5009 SetupAllViewSelection(0, nLastViewLine);
5011 CString sLine = GetViewLineChars(nLastViewLine);
5012 m_ptSelectionViewPosStart = SetupPoint(0, 0);
5013 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
5014 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
5016 UpdateWindow();
5019 void CBaseView::FilterWhitespaces(CString& first, CString& second)
5021 FilterWhitespaces(first);
5022 FilterWhitespaces(second);
5025 void CBaseView::FilterWhitespaces(CString& line)
5027 line.Remove(' ');
5028 line.Remove('\t');
5029 line.Remove('\r');
5030 line.Remove('\n');
5033 int CBaseView::GetButtonEventLineIndex(const POINT& point)
5035 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
5036 int nEventLine = nLineFromTop + m_nTopLine;
5037 nEventLine--; //we need the index
5038 return nEventLine;
5042 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
5044 if (RelayTrippleClick(pMsg))
5045 return TRUE;
5046 return CView::PreTranslateMessage(pMsg);
5050 void CBaseView::ResetUndoStep()
5052 m_AllState.Clear();
5055 void CBaseView::SaveUndoStep()
5057 if (!m_AllState.IsEmpty())
5059 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
5061 ResetUndoStep();
5064 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
5066 m_pState->addedlines.push_back(index);
5067 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
5070 void CBaseView::InsertViewData( int index, const viewdata& data )
5072 m_pState->addedlines.push_back(index);
5073 m_pViewData->InsertData(index, data);
5076 void CBaseView::RemoveViewData( int index )
5078 m_pState->removedlines[index] = m_pViewData->GetData(index);
5079 m_pViewData->RemoveData(index);
5082 void CBaseView::SetViewData( int index, const viewdata& data )
5084 m_pState->replacedlines[index] = m_pViewData->GetData(index);
5085 m_pViewData->SetData(index, data);
5088 void CBaseView::SetViewState( int index, DiffStates state )
5090 m_pState->linestates[index] = m_pViewData->GetState(index);
5091 m_pViewData->SetState(index, state);
5094 void CBaseView::SetViewLine( int index, const CString& sLine )
5096 m_pState->difflines[index] = m_pViewData->GetLine(index);
5097 m_pViewData->SetLine(index, sLine);
5100 void CBaseView::SetViewLineNumber( int index, int linenumber )
5102 int oldLineNumber = m_pViewData->GetLineNumber(index);
5103 if (oldLineNumber != linenumber) {
5104 m_pState->linelines[index] = oldLineNumber;
5105 m_pViewData->SetLineNumber(index, linenumber);
5109 void CBaseView::SetViewLineEnding( int index, EOL ending )
5111 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
5112 m_pViewData->SetLineEnding(index, ending);
5115 void CBaseView::SetViewMarked( int index, bool marked )
5117 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5118 m_pViewData->SetMarked(index, marked);
5122 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5124 if (HasSelection())
5126 start = m_nSelViewBlockStart;
5127 end = m_nSelViewBlockEnd;
5128 return true;
5130 return false;
5133 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5135 RebuildIfNecessary();
5136 if ((size() <= screenLine) || (screenLine < 0))
5137 return 0;
5138 return m_Screen2View[screenLine].nViewLine;
5141 int CBaseView::Screen2View::size()
5143 RebuildIfNecessary();
5144 return static_cast<int>(m_Screen2View.size());
5147 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5149 RebuildIfNecessary();
5150 if (size() <= screenLine)
5151 return 0;
5152 return m_Screen2View[screenLine].nViewSubLine;
5155 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5157 RebuildIfNecessary();
5158 return m_Screen2View[screenLine];
5162 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5164 void CBaseView::Screen2View::RebuildIfNecessary()
5166 if (!m_pViewData)
5167 return; // rebuild not necessary
5169 FixScreenedCacheSize(m_pwndLeft);
5170 FixScreenedCacheSize(m_pwndRight);
5171 FixScreenedCacheSize(m_pwndBottom);
5172 if (!m_bFull)
5174 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5176 ResetScreenedViewLineCache(m_pwndLeft, *it);
5177 ResetScreenedViewLineCache(m_pwndRight, *it);
5178 ResetScreenedViewLineCache(m_pwndBottom, *it);
5181 else
5183 ResetScreenedViewLineCache(m_pwndLeft);
5184 ResetScreenedViewLineCache(m_pwndRight);
5185 ResetScreenedViewLineCache(m_pwndBottom);
5187 m_RebuildRanges.clear();
5188 m_bFull = false;
5190 size_t OldSize = m_Screen2View.size();
5191 m_Screen2View.clear();
5192 m_Screen2View.reserve(OldSize); // guess same size
5193 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5195 if (m_pMainFrame->m_bCollapsed)
5197 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5198 ++i;
5199 if (!(i < m_pViewData->GetCount()))
5200 break;
5202 TScreenLineInfo oLineInfo;
5203 oLineInfo.nViewLine = i;
5204 oLineInfo.nViewSubLine = -1; // no wrap
5205 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5207 int nMaxLines = 0;
5208 if (IsLeftViewGood())
5209 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5210 if (IsRightViewGood())
5211 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5212 if (IsBottomViewGood())
5213 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5214 for (int l = 0; l < (nMaxLines-1); ++l)
5216 oLineInfo.nViewSubLine++;
5217 m_Screen2View.push_back(oLineInfo);
5219 oLineInfo.nViewSubLine++;
5221 m_Screen2View.push_back(oLineInfo);
5223 m_pViewData = nullptr;
5225 if (IsLeftViewGood())
5226 m_pwndLeft->BuildMarkedWordArray();
5227 if (IsRightViewGood())
5228 m_pwndRight->BuildMarkedWordArray();
5229 if (IsBottomViewGood())
5230 m_pwndBottom->BuildMarkedWordArray();
5231 UpdateLocator();
5232 RecalcAllVertScrollBars();
5233 RecalcAllHorzScrollBars();
5236 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5238 RebuildIfNecessary();
5240 int nScreenLineCount = static_cast<int>(m_Screen2View.size());
5242 int nPos = 0;
5243 if (nScreenLineCount>16)
5245 // for enough long data search for last screen
5246 // with viewline less than one we are looking for
5247 // use approximate method (based on) binary search using asymmetric start point
5248 // in form 2**n (determined as MSB of length) to go around division and rounding;
5249 // this effectively looks for bit values from MSB to LSB
5251 int nTestBit;
5252 //GetMostSignificantBitValue
5253 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5254 nTestBit = nScreenLineCount;
5255 nTestBit |= nTestBit>>1;
5256 nTestBit |= nTestBit>>2;
5257 nTestBit |= nTestBit>>4;
5258 nTestBit |= nTestBit>>8;
5259 nTestBit |= nTestBit>>16;
5260 nTestBit ^= (nTestBit>>1);
5262 while (nTestBit)
5264 int nTestPos = nPos | nTestBit;
5265 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5267 nPos = nTestPos;
5269 nTestBit >>= 1;
5272 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5274 nPos++;
5277 return nPos;
5280 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5281 m_bFull = true;
5283 m_pViewData = pViewData;
5286 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5288 if (m_bFull)
5289 return;
5291 m_pViewData = pViewData;
5293 TRebuildRange Range;
5294 Range.FirstViewLine=nFirstViewLine;
5295 Range.LastViewLine=nLastViewLine;
5296 m_RebuildRanges.push_back(Range);
5299 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5301 if (!IsViewGood(pwndView))
5303 return false;
5305 const int nOldSize = static_cast<int>(pwndView->m_ScreenedViewLine.size());
5306 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5307 if (nOldSize == nViewCount)
5309 return false;
5311 pwndView->m_ScreenedViewLine.resize(nViewCount);
5312 return true;
5315 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5317 if (!IsViewGood(pwndView))
5319 return false;
5321 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5322 ResetScreenedViewLineCache(pwndView, Range);
5323 return true;
5326 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5328 if (!IsViewGood(pwndView))
5330 return false;
5332 if (Range.LastViewLine == -1)
5334 return false;
5336 ASSERT(Range.FirstViewLine >= 0);
5337 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5338 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5340 pwndView->m_ScreenedViewLine[i].Clear();
5342 return false;
5345 void CBaseView::WrapChanged()
5347 m_nMaxLineLength = -1;
5348 m_nOffsetChar = 0;
5349 RecalcHorzScrollBar();
5352 void CBaseView::OnEditFind()
5354 if (m_pFindDialog)
5355 return;
5357 int id = 0;
5358 if (this == m_pwndLeft)
5359 id = 1;
5360 if (this == m_pwndRight)
5361 id = 2;
5362 if (this == m_pwndBottom)
5363 id = 3;
5365 m_pFindDialog = new CFindDlg(this);
5366 m_pFindDialog->Create(this, id);
5368 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5369 m_pFindDialog->SetReadonly(m_bReadonly);
5372 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5374 ASSERT(m_pFindDialog != nullptr);
5376 if (m_pFindDialog->IsTerminating())
5378 // invalidate the handle identifying the dialog box.
5379 m_pFindDialog = nullptr;
5380 return 0;
5383 if(m_pFindDialog->FindNext())
5385 //read data from dialog
5386 m_sFindText = m_pFindDialog->GetFindString();
5387 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5388 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5389 m_bWholeWord = m_pFindDialog->WholeWord();
5391 if (!m_bMatchCase)
5392 m_sFindText = m_sFindText.MakeLower();
5394 BuildFindStringArray();
5395 if (static_cast<CFindDlg::FindType>(wParam) == CFindDlg::FindType::Find)
5397 if (m_pFindDialog->SearchUp())
5398 OnEditFindprev();
5399 else
5400 OnEditFindnext();
5402 else if (static_cast<CFindDlg::FindType>(wParam) == CFindDlg::FindType::Count)
5404 size_t count = 0;
5405 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5406 count += m_arFindStringLines[i];
5407 CString matches;
5408 matches.Format(IDS_FIND_COUNT, count);
5409 m_pFindDialog->SetStatusText(matches);
5411 else if (static_cast<CFindDlg::FindType>(wParam) == CFindDlg::FindType::Replace)
5413 if (!IsWritable())
5414 return 0;
5415 bool bFound = false;
5416 if (m_pFindDialog->SearchUp())
5417 bFound = Search(SearchPrevious, true, true, false);
5418 else
5419 bFound = Search(SearchNext, true, true, false);
5420 if (bFound)
5422 CString sReplaceText = m_pFindDialog->GetReplaceString();
5423 CUndo::GetInstance().BeginGrouping();
5424 RemoveSelectedText();
5425 InsertText(sReplaceText);
5426 CUndo::GetInstance().EndGrouping();
5430 else if (static_cast<CFindDlg::FindType>(wParam) == CFindDlg::FindType::ReplaceAll)
5432 if (!IsWritable())
5433 return 0;
5434 bool bFound = false;
5435 int replaceCount = 0;
5436 POINT lastPoint = m_ptSelectionViewPosStart;
5437 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5438 CUndo::GetInstance().BeginGrouping();
5441 bFound = Search(SearchNext, true, false, true);
5442 if (bFound)
5444 CString sReplaceText = m_pFindDialog->GetReplaceString();
5445 RemoveSelectedText();
5446 InsertText(sReplaceText);
5447 ++replaceCount;
5449 } while (bFound);
5450 CUndo::GetInstance().EndGrouping();
5451 if (replaceCount == 0)
5452 m_ptSelectionViewPosStart = lastPoint;
5453 CString message;
5454 message.Format(IDS_FIND_REPLACED, replaceCount);
5455 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5459 return 0;
5462 void CBaseView::OnEditFindnextStart()
5464 if (!m_pViewData)
5465 return;
5466 if (HasTextSelection())
5468 m_sFindText = GetSelectedText();
5469 m_bMatchCase = false;
5470 m_bLimitToDiff = false;
5471 m_bWholeWord = false;
5472 m_sFindText = m_sFindText.MakeLower();
5474 BuildFindStringArray();
5475 OnEditFindnext();
5477 else
5479 m_sFindText.Empty();
5480 BuildFindStringArray();
5484 void CBaseView::OnEditFindprevStart()
5486 if (!m_pViewData)
5487 return;
5488 if (HasTextSelection())
5490 m_sFindText = GetSelectedText();
5491 m_bMatchCase = false;
5492 m_bLimitToDiff = false;
5493 m_bWholeWord = false;
5494 m_sFindText = m_sFindText.MakeLower();
5496 BuildFindStringArray();
5497 OnEditFindprev();
5499 else
5501 m_sFindText.Empty();
5502 BuildFindStringArray();
5506 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5508 if (srchDir == SearchPrevious)
5510 int laststart = -1;
5511 int laststart2 = -1;
5514 laststart2 = laststart;
5515 laststart = str.Find(m_sFindText, laststart + 1);
5516 } while (laststart >= 0 && laststart < start);
5517 start = laststart2;
5519 else
5520 start = str.Find(m_sFindText, start);
5521 end = start + m_sFindText.GetLength();
5522 bool bStringFound = (start >= 0);
5523 if (bStringFound && m_bWholeWord)
5525 if (start)
5526 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5528 if (bStringFound)
5530 if (str.GetLength() > end)
5531 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5534 return bStringFound;
5537 void CBaseView::OnEditFindprev()
5539 Search(SearchPrevious, false, true, false);
5542 void CBaseView::OnEditFindnext()
5544 Search(SearchNext, false, true, false);
5547 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5549 if (m_sFindText.IsEmpty())
5550 return false;
5551 if(!m_pViewData)
5552 return false;
5554 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5555 POINT end;
5556 end.y = m_pViewData->GetCount()-1;
5557 if (end.y < 0)
5558 return false;
5560 if (srchDir==SearchNext)
5561 end.x = GetViewLineLength(end.y);
5562 else
5564 end.x = m_ptSelectionViewPosStart.x;
5565 start.x = 0;
5568 if (!HasTextSelection())
5570 start.y = m_ptCaretViewPos.y;
5571 if (srchDir==SearchNext)
5572 start.x = m_ptCaretViewPos.x;
5573 else
5575 start.x = 0;
5576 end.x = m_ptCaretViewPos.x;
5579 CString sSelectedText;
5580 int startline = -1;
5581 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5583 if (nViewLine < 0)
5585 if (stopEof)
5586 return false;
5587 nViewLine = m_pViewData->GetCount()-1;
5588 startline = start.y;
5589 if (flashIfNotFound)
5591 if (m_pFindDialog)
5592 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5593 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5596 if (nViewLine > end.y)
5598 if (stopEof)
5599 return false;
5600 nViewLine = 0;
5601 startline = start.y;
5602 if (flashIfNotFound)
5604 if (m_pFindDialog)
5605 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5606 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5609 switch (m_pViewData->GetState(nViewLine))
5611 case DIFFSTATE_EMPTY:
5612 break;
5613 case DIFFSTATE_UNKNOWN:
5614 case DIFFSTATE_NORMAL:
5615 case DIFFSTATE_FILTEREDDIFF:
5616 if (m_bLimitToDiff)
5617 break;
5618 case DIFFSTATE_REMOVED:
5619 case DIFFSTATE_REMOVEDWHITESPACE:
5620 case DIFFSTATE_ADDED:
5621 case DIFFSTATE_ADDEDWHITESPACE:
5622 case DIFFSTATE_WHITESPACE:
5623 case DIFFSTATE_WHITESPACE_DIFF:
5624 case DIFFSTATE_CONFLICTED:
5625 case DIFFSTATE_CONFLICTED_IGNORED:
5626 case DIFFSTATE_CONFLICTADDED:
5627 case DIFFSTATE_CONFLICTEMPTY:
5628 case DIFFSTATE_CONFLICTRESOLVED:
5629 case DIFFSTATE_IDENTICALREMOVED:
5630 case DIFFSTATE_IDENTICALADDED:
5631 case DIFFSTATE_THEIRSREMOVED:
5632 case DIFFSTATE_THEIRSADDED:
5633 case DIFFSTATE_YOURSREMOVED:
5634 case DIFFSTATE_YOURSADDED:
5635 case DIFFSTATE_EDITED:
5637 sSelectedText = GetViewLineChars(nViewLine);
5638 if (nViewLine == start.y && startline < 0)
5639 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5640 if (!m_bMatchCase)
5641 sSelectedText = sSelectedText.MakeLower();
5642 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5643 int endfound = 0;
5644 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5646 HighlightViewLines(nViewLine, nViewLine);
5647 m_ptSelectionViewPosStart.x = startfound;
5648 m_ptSelectionViewPosEnd.x = endfound;
5649 if (nViewLine == start.y && startline < 0)
5651 m_ptSelectionViewPosStart.x += start.x;
5652 m_ptSelectionViewPosEnd.x += start.x;
5654 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5655 m_ptSelectionViewPosStart.y = nViewLine;
5656 m_ptSelectionViewPosEnd.y = nViewLine;
5657 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5658 UpdateViewsCaretPosition();
5659 EnsureCaretVisible();
5660 Invalidate();
5661 return true;
5664 break;
5667 if (startline >= 0)
5669 if (nViewLine == startline)
5671 if (flashIfNotFound)
5673 CString message;
5674 message.Format(IDS_FIND_NOTFOUND, static_cast<LPCTSTR>(m_sFindText));
5675 if (m_pFindDialog)
5676 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5677 ::MessageBeep(0xFFFFFFFF);
5678 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5680 break;
5684 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5685 return false;
5688 CString CBaseView::GetSelectedText() const
5690 CString sSelectedText;
5691 POINT start = m_ptSelectionViewPosStart;
5692 POINT end = m_ptSelectionViewPosEnd;
5693 if (!HasTextSelection())
5695 if (!HasSelection())
5696 return sSelectedText;
5697 start.y = m_nSelViewBlockStart;
5698 start.x = 0;
5699 end.y = m_nSelViewBlockEnd;
5700 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5702 if (!m_pViewData)
5703 return sSelectedText;
5704 // first store the selected lines in one CString
5705 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5707 switch (m_pViewData->GetState(nViewLine))
5709 case DIFFSTATE_EMPTY:
5710 break;
5711 case DIFFSTATE_UNKNOWN:
5712 case DIFFSTATE_NORMAL:
5713 case DIFFSTATE_REMOVED:
5714 case DIFFSTATE_REMOVEDWHITESPACE:
5715 case DIFFSTATE_ADDED:
5716 case DIFFSTATE_ADDEDWHITESPACE:
5717 case DIFFSTATE_WHITESPACE:
5718 case DIFFSTATE_WHITESPACE_DIFF:
5719 case DIFFSTATE_CONFLICTED:
5720 case DIFFSTATE_CONFLICTED_IGNORED:
5721 case DIFFSTATE_CONFLICTADDED:
5722 case DIFFSTATE_CONFLICTEMPTY:
5723 case DIFFSTATE_CONFLICTRESOLVED:
5724 case DIFFSTATE_IDENTICALREMOVED:
5725 case DIFFSTATE_IDENTICALADDED:
5726 case DIFFSTATE_THEIRSREMOVED:
5727 case DIFFSTATE_THEIRSADDED:
5728 case DIFFSTATE_YOURSREMOVED:
5729 case DIFFSTATE_YOURSADDED:
5730 case DIFFSTATE_EDITED:
5731 case DIFFSTATE_FILTEREDDIFF:
5732 sSelectedText += GetViewLineChars(nViewLine);
5733 sSelectedText += L"\r\n";
5734 break;
5737 // remove the non-selected chars from the first line, last line and last \r\n
5738 int nLeftCut = start.x;
5739 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5740 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5741 return sSelectedText;
5744 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods, bool& hasFilteredMods)
5746 hasMods = false;
5747 hasConflicts = false;
5748 hasWhitespaceMods = false;
5749 hasFilteredMods = false;
5751 if (m_pViewData)
5753 for (int i=0; i<m_pViewData->GetCount(); i++)
5755 DiffStates state = m_pViewData->GetState(i);
5756 switch (state)
5758 case DIFFSTATE_ADDED:
5759 case DIFFSTATE_IDENTICALADDED:
5760 case DIFFSTATE_THEIRSADDED:
5761 case DIFFSTATE_YOURSADDED:
5762 case DIFFSTATE_CONFLICTADDED:
5763 case DIFFSTATE_IDENTICALREMOVED:
5764 case DIFFSTATE_REMOVED:
5765 case DIFFSTATE_THEIRSREMOVED:
5766 case DIFFSTATE_YOURSREMOVED:
5767 case DIFFSTATE_EMPTY:
5768 hasMods = true;
5769 break;
5770 case DIFFSTATE_CONFLICTED:
5771 case DIFFSTATE_CONFLICTED_IGNORED:
5772 hasConflicts = true;
5773 break;
5774 case DIFFSTATE_REMOVEDWHITESPACE:
5775 case DIFFSTATE_ADDEDWHITESPACE:
5776 case DIFFSTATE_WHITESPACE:
5777 case DIFFSTATE_WHITESPACE_DIFF:
5778 hasWhitespaceMods = true;
5779 break;
5780 case DIFFSTATE_FILTEREDDIFF:
5781 hasFilteredMods = true;
5782 break;
5788 void CBaseView::OnEditGotoline()
5790 if (!m_pViewData)
5791 return;
5792 // find the last and first line number
5793 int nViewLineCount = m_pViewData->GetCount();
5795 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5796 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5798 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5799 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5801 break;
5804 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5806 return;
5808 nLastLineNumber++;
5809 int nFirstLineNumber=1; // first is always 1
5811 CString sText;
5812 sText.FormatMessage(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5814 CGotoLineDlg dlg(this);
5815 dlg.SetLabel(sText);
5816 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5817 if (dlg.DoModal() == IDOK)
5819 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5821 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5823 HighlightViewLines(nViewLine, nViewLine);
5824 return;
5830 int CBaseView::SaveFile(int nFlags)
5832 Invalidate();
5833 if (m_pViewData && m_pWorkingFile)
5835 CFileTextLines file;
5836 m_SaveParams.m_LineEndings = m_lineendings;
5837 m_SaveParams.m_UnicodeType = m_texttype;
5838 file.SetSaveParams(m_SaveParams);
5840 for (int i=0; i<m_pViewData->GetCount(); i++)
5842 //only copy non-removed lines
5843 DiffStates state = m_pViewData->GetState(i);
5844 switch (state)
5846 case DIFFSTATE_CONFLICTED:
5847 case DIFFSTATE_CONFLICTED_IGNORED:
5849 int first = i;
5850 int last = i;
5853 last++;
5854 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5855 file.Add(L"<<<<<<< .mine", EOL_NOENDING);
5856 for (int j=first; j<last; j++)
5858 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5860 file.Add(L"=======", EOL_NOENDING);
5861 for (int j=first; j<last; j++)
5863 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5865 file.Add(L">>>>>>> .theirs", EOL_NOENDING);
5866 i = last-1;
5868 break;
5869 case DIFFSTATE_EMPTY:
5870 break;
5871 case DIFFSTATE_CONFLICTEMPTY:
5872 case DIFFSTATE_IDENTICALREMOVED:
5873 case DIFFSTATE_REMOVED:
5874 case DIFFSTATE_THEIRSREMOVED:
5875 case DIFFSTATE_YOURSREMOVED:
5876 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5877 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5879 // do not save removed lines
5880 break;
5882 default:
5883 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5884 break;
5887 CString filename = m_pWorkingFile->GetFilename();
5888 if (m_pWorkingFile->IsReadonly())
5889 if (!CCommonAppUtils::FileOpenSave(filename, nullptr, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5890 return -1;
5891 if (!file.Save(filename))
5893 ::MessageBox(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
5894 return -1;
5896 m_pWorkingFile->SetFileName(filename);
5897 m_pWorkingFile->StoreFileAttributes();
5898 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5899 SetModified(FALSE);
5900 CUndo::GetInstance().MarkAsOriginalState(
5901 this == m_pwndLeft,
5902 this == m_pwndRight,
5903 this == m_pwndBottom);
5904 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5905 return 0;
5906 return file.GetCount();
5908 return 1;
5912 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5914 if (m_pWorkingFile)
5916 m_pWorkingFile->SetFileName(sFileName);
5917 return SaveFile(nFlags);
5919 return -1;
5923 EOL CBaseView::GetLineEndings()
5925 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5928 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5930 if (bHasMixedEols)
5932 return EOL_AUTOLINE; // mixed eols - hack value
5934 if (m_lineendings == EOL_AUTOLINE)
5936 return EOL_CRLF;
5938 return m_lineendings;
5941 void CBaseView::ReplaceLineEndings(EOL eEol)
5943 if (eEol == EOL_AUTOLINE)
5945 return;
5947 // set AUTOLINE
5948 m_lineendings = eEol;
5949 // replace all set EOLs
5950 // TODO store line endings and lineendings in undo
5951 //CUndo::BeginGrouping();
5952 for (int i = 0; i < GetViewCount(); ++i)
5954 if (IsLineEmpty(i))
5956 continue;
5958 EOL eLineEol = GetViewLineEnding(i);
5959 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5961 continue;
5963 SetViewLineEnding(i, eEol);
5965 //CUndo::EndGrouping();
5966 //CUndo::saveundostep;
5967 DocumentUpdated();
5968 SetModified();
5971 void CBaseView::SetLineEndingStyle(EOL eEol)
5973 m_lineendings = eEol;
5976 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5978 if (m_texttype == eTextType)
5980 return;
5982 m_texttype = eTextType;
5983 DocumentUpdated();
5984 SetModified();
5987 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5989 if (IsReadonly())
5990 return; // nothing to be changed in read-only view
5991 CEncodingDlg dlg;
5992 dlg.view.LoadString(nTextId);
5993 dlg.texttype = m_texttype;
5994 dlg.lineendings = GetLineEndings();
5995 if (dlg.DoModal() != IDOK)
5996 return;
5997 SetTextType(dlg.texttype);
5998 ReplaceLineEndings(dlg.lineendings);
6002 Replaces lines from source view to this
6004 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
6006 if (!IsViewGood(pwndView))
6007 return;
6008 if (!IsWritable())
6009 return;
6010 CUndo::GetInstance().BeginGrouping();
6012 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6014 bool skip = fnSkip(viewLine);
6015 if (skip)
6017 if (GetViewMarked(viewLine))
6018 SetViewMarked(viewLine, false);
6019 continue;
6021 viewdata line = pwndView->GetViewData(viewLine);
6022 if (line.ending != EOL_NOENDING)
6023 line.ending = m_lineendings;
6024 switch (line.state)
6026 case DIFFSTATE_CONFLICTEMPTY:
6027 case DIFFSTATE_UNKNOWN:
6028 line.state = DIFFSTATE_EMPTY;
6029 case DIFFSTATE_EMPTY:
6030 break;
6031 case DIFFSTATE_ADDED:
6032 case DIFFSTATE_CONFLICTADDED:
6033 case DIFFSTATE_CONFLICTED:
6034 case DIFFSTATE_CONFLICTED_IGNORED:
6035 case DIFFSTATE_IDENTICALADDED:
6036 case DIFFSTATE_THEIRSADDED:
6037 case DIFFSTATE_YOURSADDED:
6038 case DIFFSTATE_IDENTICALREMOVED:
6039 case DIFFSTATE_REMOVED:
6040 case DIFFSTATE_THEIRSREMOVED:
6041 case DIFFSTATE_YOURSREMOVED:
6042 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
6043 line.state = DIFFSTATE_NORMAL;
6044 case DIFFSTATE_NORMAL:
6045 break;
6046 default:
6047 break;
6049 bool marked = GetViewMarked(viewLine);
6050 SetViewData(viewLine, line);
6051 if (marked)
6052 SetViewMarked(viewLine, false);
6053 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
6055 // if this view is in ASCII and the other is not, we have to make sure that
6056 // the text we copy from the other view can actually be saved in ASCII encoding.
6057 // if not, we have to change this views encoding to the same encoding as the other view
6058 BOOL useDefault = FALSE;
6059 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, nullptr, 0, 0, &useDefault);
6060 if (useDefault) // a default char is required, so the char can not be saved as ASCII
6061 SetTextType(pwndView->GetTextType());
6064 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6065 // TODO: check if copied line is same as original one set modified only when differ
6066 SetModified();
6067 SaveUndoStep();
6069 int nRemovedLines = CleanEmptyLines();
6070 SaveUndoStep();
6071 //VerifyEols();
6072 // make sure all non empty line have EOL set but last
6073 // wrong can be last copied line(have eol, but no line under),
6074 // or old last line (line before copied block missing eol, but have line under)
6075 // we'll check all lines to be sure
6076 int nLine = GetViewCount();
6077 // check last line have no EOL set
6078 while (--nLine>=0)
6080 if (!IsViewLineEmpty(nLine))
6082 if (GetViewLineEnding(nLine) != EOL_NOENDING)
6084 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6085 // so next line is empty
6086 ASSERT(IsViewLineEmpty(nLine+1));
6087 // and we can turn it to normal empty line
6088 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
6090 break;
6093 // check all (nonlast) line have EOL set
6094 while (--nLine>=0)
6096 if (!IsViewLineEmpty(nLine))
6098 if (GetViewLineEnding(nLine) == EOL_NOENDING)
6100 SetViewLineEnding(nLine, m_lineendings);
6101 // in theory there should be only one line needing fix, but most of time we get over all anyway
6102 // break;
6106 SaveUndoStep();
6107 UpdateViewLineNumbers();
6108 SaveUndoStep();
6110 CUndo::GetInstance().EndGrouping();
6112 if (nRemovedLines!=0)
6114 // some lines are gone update selection
6115 ClearSelection();
6116 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6118 BuildAllScreen2ViewVector();
6119 pwndView->Invalidate();
6120 RefreshViews();
6123 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6125 if (!IsWritable())
6126 return;
6127 CUndo::GetInstance().BeginGrouping();
6129 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6130 SetViewMarked(viewLine, marked);
6132 SetModified();
6133 SaveUndoStep();
6134 CUndo::GetInstance().EndGrouping();
6136 BuildAllScreen2ViewVector();
6137 Invalidate();
6138 RefreshViews();
6141 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6143 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6144 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6147 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6149 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6150 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6153 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6155 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6156 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6159 int CBaseView::GetIndentCharsForLine(int x, int y)
6161 const int maxGuessLine = 100;
6162 int nTabMode = -1;
6163 const CString& line = GetViewLine(y);
6164 if (m_nTabMode & TABMODE_SMARTINDENT)
6166 // if the line contains one tab, use tabs
6167 // we can not test for spaces, since even if tabs are used,
6168 // spaces are used in a tabified file for alignment.
6169 if (line.Find(L'\t') >= 0)
6170 nTabMode = 0; // use tabs
6171 else if (line.GetLength() > m_nTabSize)
6172 nTabMode = 1; // use spaces
6174 if (m_nTabMode & TABMODE_SMARTINDENT)
6176 // detect lines nearby
6177 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6179 bool above = i > 0 && i >= y - maxGuessLine;
6180 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6181 if (!(above || below))
6182 break;
6183 auto ac = GetViewLine(i);
6184 auto bc = GetViewLine(j);
6185 if ((ac.Find(L'\t') >= 0) || (bc.Find(L'\t') >= 0))
6187 nTabMode = 0;
6188 break;
6190 else if ((ac.GetLength() > m_nTabSize) && (bc.GetLength() > m_nTabSize))
6192 nTabMode = 1;
6193 break;
6198 else
6199 nTabMode = m_nTabMode & TABMODE_USESPACES;
6201 if (nTabMode > 0)
6203 // use spaces
6204 x = CountExpandedChars(line, x);
6205 return (m_nTabSize - (x % m_nTabSize));
6208 // use tab
6209 return 0;
6212 void CBaseView::AddIndentationForSelectedBlock()
6214 bool bModified = false;
6215 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6217 // skip the line if no character is selected in the last selected line
6218 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6220 continue;
6222 // skip empty lines
6223 if (IsLineEmpty(nViewLine))
6225 continue;
6227 const CString &sLine = GetViewLine(nViewLine);
6228 CString sTemp = sLine;
6229 if (sTemp.Trim().IsEmpty())
6231 // skip empty and whitechar only lines
6232 continue;
6234 // add tab to line start (alternatively m_nTabSize spaces can be used)
6235 CString tabStr;
6236 int indentChars = GetIndentCharsForLine(0, nViewLine);
6237 tabStr = indentChars > 0 ? CString(L' ', indentChars) : L"\t";
6238 SetViewLine(nViewLine, tabStr + sLine);
6239 bModified = true;
6241 if (bModified)
6243 SetModified();
6244 SaveUndoStep();
6245 BuildAllScreen2ViewVector();
6249 void CBaseView::RemoveIndentationForSelectedBlock()
6251 bool bModified = false;
6252 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6254 // skip the line if no character is selected in the last selected line
6255 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6257 continue;
6259 // skip empty lines
6260 if (IsLineEmpty(nViewLine))
6262 continue;
6264 CString sLine = GetViewLine(nViewLine);
6265 // remove up to n spaces from line start
6266 // and one tab (if less then n spaces was removed)
6267 int nPos = 0;
6268 while (nPos<m_nTabSize)
6270 switch (sLine[nPos])
6272 case ' ':
6273 nPos++;
6274 continue;
6275 case '\t':
6276 nPos++;
6278 break;
6280 if (nPos>0)
6282 sLine.Delete(0, nPos);
6283 SetViewLine(nViewLine, sLine);
6284 bModified = true;
6287 if (bModified)
6289 SetModified();
6290 SaveUndoStep();
6291 BuildAllScreen2ViewVector();
6296 there are two possible versions
6297 - convert tabs to spaces only in front of text (implemented)
6298 - convert all tabs to spaces
6300 void CBaseView::ConvertTabToSpaces()
6302 bool bModified = false;
6303 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6305 if (IsLineEmpty(nViewLine))
6307 continue;
6309 const CString &sLine = GetViewLine(nViewLine);
6310 bool bTabToConvertFound = false;
6311 int nPosIn = 0;
6312 int nPosOut = 0;
6313 while (nPosIn<sLine.GetLength())
6315 switch (sLine[nPosIn])
6317 case ' ':
6318 nPosIn++;
6319 nPosOut++;
6320 continue;
6321 case '\t':
6322 nPosIn++;
6323 bTabToConvertFound = true;
6324 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6325 continue;
6327 break;
6329 if (bTabToConvertFound)
6331 CString sLineNew = sLine;
6332 sLineNew.Delete(0, nPosIn);
6333 sLineNew = CString(' ', nPosOut) + sLineNew;
6334 SetViewLine(nViewLine, sLineNew);
6335 bModified = true;
6338 if (bModified)
6340 SetModified();
6341 SaveUndoStep();
6342 BuildAllScreen2ViewVector();
6347 there are two possible version
6348 - convert spaces to tabs only in front of text (implemented)
6349 - convert all spaces to tabs
6351 void CBaseView::Tabularize()
6353 bool bModified = false;
6354 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6356 if (IsLineEmpty(nViewLine))
6358 continue;
6360 const CString &sLine = GetViewLine(nViewLine);
6361 int nDel = 0;
6362 int nTabCount = 0; // total tabs to be used
6363 int nSpaceCount = 0; // number of spaces in tab size run
6364 int nPos = 0;
6365 while (nPos<sLine.GetLength())
6367 switch (sLine[nPos++])
6369 case ' ':
6370 //bSpace = true;
6371 if (++nSpaceCount < m_nTabSize)
6373 continue;
6375 case '\t':
6376 nTabCount++;
6377 nSpaceCount = 0;
6378 nDel = nPos;
6379 continue;
6381 break;
6383 if (nDel > 0)
6385 CString sLineNew = sLine;
6386 sLineNew.Delete(0, nDel);
6387 sLineNew = CString('\t', nTabCount) + sLineNew;
6388 if (sLine!=sLineNew)
6390 SetViewLine(nViewLine, sLineNew);
6391 bModified = true;
6395 if (bModified)
6397 SetModified();
6398 SaveUndoStep();
6399 BuildAllScreen2ViewVector();
6403 void CBaseView::RemoveTrailWhiteChars()
6405 bool bModified = false;
6406 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6408 if (IsLineEmpty(nViewLine))
6410 continue;
6412 const CString &sLine = GetViewLine(nViewLine);
6413 CString sLineNew = sLine;
6414 sLineNew.TrimRight();
6415 if (sLine.GetLength()!=sLineNew.GetLength())
6417 SetViewLine(nViewLine, sLineNew);
6418 bModified = true;
6421 if (bModified)
6423 SetModified();
6424 SaveUndoStep();
6425 BuildAllScreen2ViewVector();
6429 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6431 if (GetViewCount()>10000)
6433 // 10k lines is enough to check
6434 TWhitecharsProperties oRet = {true, true, true, true};
6435 return oRet;
6437 TWhitecharsProperties oRet = {};
6438 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6440 if (IsLineEmpty(nViewLine))
6442 continue;
6444 const CString &sLine = GetViewLine(nViewLine);
6445 if (sLine.IsEmpty())
6447 continue;
6449 // check leading whites for convertible tabs and spaces
6450 int nPos = 0;
6451 int nSpaceCount = 0; // number of spaces in tab size run
6452 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6454 switch (sLine[nPos++])
6456 case ' ':
6457 if (++nSpaceCount >= m_nTabSize)
6459 oRet.HasSpacesToConvert = true;
6461 continue;
6462 case '\t':
6463 oRet.HasTabsToConvert = true;
6464 if (nSpaceCount!=0)
6466 oRet.HasSpacesToConvert = true;
6468 continue;
6470 break;
6473 // check trailing whites for removable chars
6474 switch (sLine[sLine.GetLength()-1])
6476 case ' ':
6477 case '\t':
6478 oRet.HasTrailWhiteChars = true;
6481 // check EOLs
6482 EOL eLineEol = GetViewLineEnding(nViewLine);
6483 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6485 oRet.HasMixedEols = true;
6488 return oRet;
6491 void CBaseView::InsertText(const CString& sText)
6493 ResetUndoStep();
6495 POINT ptCaretViewPos = GetCaretViewPosition();
6496 int nLeft = ptCaretViewPos.x;
6497 int nViewLine = ptCaretViewPos.y;
6499 if ((nViewLine == 0) && (GetViewCount() == 0))
6500 OnChar(VK_RETURN, 0, 0);
6502 std::vector<CString> lines;
6503 int nStart = 0;
6504 int nEolPos = 0;
6505 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6507 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6508 lines.push_back(sLine);
6509 nEolPos++;
6510 nStart = nEolPos;
6512 CString sLine = sText.Mid(nStart);
6513 lines.push_back(sLine);
6515 int nLinesToPaste = static_cast<int>(lines.size());
6516 if (nLinesToPaste > 1)
6518 // multiline text
6520 // We want to undo the multiline insertion in a single step.
6521 CUndo::GetInstance().BeginGrouping();
6523 sLine = GetViewLineChars(nViewLine);
6524 CString sLineLeft = sLine.Left(nLeft);
6525 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6526 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6527 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6528 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6530 newLine.sLine = sLineLeft + lines[0];
6531 SetViewData(nViewLine, newLine);
6534 int nInsertLine = nViewLine;
6535 for (int i = 1; i < nLinesToPaste - 1; i++)
6537 newLine.sLine = lines[i];
6538 InsertViewData(++nInsertLine, newLine);
6540 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6541 newLine.ending = eOriginalEnding;
6542 InsertViewData(++nInsertLine, newLine);
6544 SetModified();
6545 SaveUndoStep();
6547 // adds new lines everywhere except me
6548 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6550 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6552 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6554 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6556 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6558 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6560 SaveUndoStep();
6562 UpdateViewLineNumbers();
6563 CUndo::GetInstance().EndGrouping();
6565 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6567 else
6569 // single line text - just insert it
6570 sLine = GetViewLineChars(nViewLine);
6571 sLine.Insert(nLeft, sText);
6572 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6573 SetViewLine(nViewLine, sLine);
6575 auto viewState = GetViewState(nViewLine);
6576 if (IsStateEmpty(viewState) || IsStateConflicted(viewState) || viewState == DIFFSTATE_IDENTICALREMOVED)
6578 // if not last line set EOL
6579 for (int nCheckViewLine = nViewLine + 1; nCheckViewLine < GetViewCount(); ++nCheckViewLine)
6581 if (!IsViewLineEmpty(nCheckViewLine))
6583 SetViewLineEnding(nViewLine, m_lineendings);
6584 break;
6587 // make sure previous (non empty) line have EOL set
6588 for (int nCheckViewLine = nViewLine - 1; nCheckViewLine > 0; --nCheckViewLine)
6590 if (!IsViewLineEmpty(nCheckViewLine))
6592 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
6593 SetViewLineEnding(nCheckViewLine, m_lineendings);
6594 break;
6599 SetViewState(nViewLine, DIFFSTATE_EDITED);
6600 SetModified();
6601 SaveUndoStep();
6604 RefreshViews();
6605 BuildAllScreen2ViewVector();
6606 UpdateCaretViewPosition(ptCaretViewPos);
6609 ULONG CBaseView::GetGestureStatus(CPoint /*ptTouch*/)
6611 return 0;