When clicking on the view border, select the whole line from the beginning
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob6ae2b8a7bc1d6d0855c5923e929d6e945788e397
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2018 - TortoiseSVN
4 // Copyright (C) 2011-2012, 2017 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 "DpiScale.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 = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4);
128 m_nTabMode = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE);
129 m_bEditorConfigEnabled = !!(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE);
130 std::fill_n(m_apFonts, fontsCount, (CFont*)nullptr);
132 int cxIcon = GetSystemMetrics(SM_CXSMICON);
133 int cyIcon = GetSystemMetrics(SM_CYSMICON);
134 m_hConflictedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
135 m_hConflictedIgnoredIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDIGNOREDLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
136 m_hRemovedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_REMOVEDLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
137 m_hAddedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ADDEDLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
138 m_hWhitespaceBlockIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WHITESPACELINE), IMAGE_ICON, cxIcon, cyIcon, 0);
139 m_hEqualIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EQUALLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
140 m_hLineEndingCR = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCR), IMAGE_ICON, cxIcon, cyIcon, 0);
141 m_hLineEndingCRLF = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCRLF), IMAGE_ICON, cxIcon, cyIcon, 0);
142 m_hLineEndingLF = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGLF), IMAGE_ICON, cxIcon, cyIcon, 0);
143 m_hEditedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEEDITED), IMAGE_ICON, cxIcon, cyIcon, 0);
144 m_hMovedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_MOVEDLINE), IMAGE_ICON, cxIcon, cyIcon, 0);
145 m_hMarkedIcon = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEMARKED), IMAGE_ICON, cxIcon, cyIcon, 0);
146 m_margincursor = (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 = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4);
255 m_nTabMode = (int)(DWORD)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 = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4);
277 m_nTabMode = (int)(DWORD)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 = (BYTE) bItalic;
500 CDC * pDC = GetDC();
501 if (pDC)
503 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\LogFontSize", 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
504 ReleaseDC(pDC);
506 wcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(L"Software\\TortoiseGitMerge\\LogFontName", L"Consolas"), _countof(m_lfBaseFont.lfFaceName) - 1);
507 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
509 delete m_apFonts[nIndex];
510 m_apFonts[nIndex] = nullptr;
511 return CView::GetFont();
514 return m_apFonts[nIndex];
517 void CBaseView::CalcLineCharDim()
519 CDC *pDC = GetDC();
520 if (!pDC)
521 return;
522 CFont *pOldFont = pDC->SelectObject(GetFont());
523 const CSize szCharExt = pDC->GetTextExtent(L"X");
524 pDC->SelectObject(pOldFont);
525 ReleaseDC(pDC);
527 m_nLineHeight = szCharExt.cy;
528 if (m_nLineHeight <= 0)
529 m_nLineHeight = -1;
530 m_nCharWidth = szCharExt.cx;
531 if (m_nCharWidth <= 0)
532 m_nCharWidth = -1;
535 int CBaseView::GetScreenChars()
537 if (m_nScreenChars == -1)
539 CRect rect;
540 GetClientRect(&rect);
541 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
542 if (m_nScreenChars < 0)
543 m_nScreenChars = 0;
545 return m_nScreenChars;
548 int CBaseView::GetAllMinScreenChars() const
550 int nChars = INT_MAX;
551 if (IsLeftViewGood())
552 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
553 if (IsRightViewGood())
554 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
555 if (IsBottomViewGood())
556 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
557 return (nChars==INT_MAX) ? 0 : nChars;
560 int CBaseView::GetAllMaxLineLength() const
562 int nLength = 0;
563 if (IsLeftViewGood())
564 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
565 if (IsRightViewGood())
566 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
567 if (IsBottomViewGood())
568 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
569 return nLength;
572 int CBaseView::GetLineHeight()
574 if (m_nLineHeight == -1)
575 CalcLineCharDim();
576 if (m_nLineHeight <= 0)
577 return 1;
578 return m_nLineHeight;
581 int CBaseView::GetCharWidth()
583 if (m_nCharWidth == -1)
584 CalcLineCharDim();
585 if (m_nCharWidth <= 0)
586 return 1;
587 return m_nCharWidth;
590 int CBaseView::GetMaxLineLength()
592 if (m_nMaxLineLength == -1)
594 m_nMaxLineLength = 0;
595 int nLineCount = GetLineCount();
596 if (nLineCount == 1)
597 return GetLineLengthWithTabsConverted(0);
598 for (int i=0; i<nLineCount; i++)
600 int nActualLength = GetLineLengthWithTabsConverted(i);
601 if (m_nMaxLineLength < nActualLength)
602 m_nMaxLineLength = nActualLength;
605 return m_nMaxLineLength;
608 int CBaseView::GetLineLengthWithTabsConverted(int index)
610 if (!m_pViewData)
611 return 0;
612 if (m_pViewData->GetCount() == 0)
613 return 0;
614 if ((int)m_Screen2View.size() <= index)
615 return 0;
616 CString sLine;
617 if (m_pMainFrame->m_bWrapLines)
618 sLine = GetLineChars(index);
619 else
621 int viewLine = GetViewLineForScreen(index);
622 sLine = m_pViewData->GetLine(viewLine);
624 int tabCount = 0;
625 wchar_t* pChar = (LPWSTR)(LPCWSTR)sLine;
626 auto nLineLength = sLine.GetLength();
627 for (int i = 0; i < nLineLength; ++i)
629 if (*pChar == '\t')
630 ++tabCount;
631 ++pChar;
633 // GetTabSize() - 1 because the tabs are already counted
634 nLineLength = nLineLength + (tabCount * (GetTabSize() - 1));
635 ASSERT(nLineLength >= 0);
636 return nLineLength;
639 int CBaseView::GetLineLength(int index)
641 if (!m_pViewData)
642 return 0;
643 if (m_pViewData->GetCount() == 0)
644 return 0;
645 if ((int)m_Screen2View.size() <= index)
646 return 0;
647 int viewLine = GetViewLineForScreen(index);
648 if (m_pMainFrame->m_bWrapLines)
650 int nLineLength = GetLineChars(index).GetLength();
651 ASSERT(nLineLength >= 0);
652 return nLineLength;
654 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
655 ASSERT(nLineLength >= 0);
656 return nLineLength;
659 int CBaseView::GetViewLineLength(int nViewLine) const
661 if (!m_pViewData)
662 return 0;
663 if (m_pViewData->GetCount() <= nViewLine)
664 return 0;
665 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
666 ASSERT(nLineLength >= 0);
667 return nLineLength;
670 int CBaseView::GetLineCount() const
672 if (!m_pViewData)
673 return 1;
674 int nLineCount = (int)m_Screen2View.size();
675 ASSERT(nLineCount >= 0);
676 return nLineCount;
679 int CBaseView::GetSubLineOffset(int index)
681 return m_Screen2View.GetSubLineOffset(index);
684 CString CBaseView::GetViewLineChars(int nViewLine) const
686 if (!m_pViewData)
687 return 0;
688 if (m_pViewData->GetCount() <= nViewLine)
689 return 0;
690 return m_pViewData->GetLine(nViewLine);
693 CString CBaseView::GetLineChars(int index)
695 if (!m_pViewData)
696 return 0;
697 if (m_pViewData->GetCount() == 0)
698 return 0;
699 if ((int)m_Screen2View.size() <= index)
700 return 0;
701 int viewLine = GetViewLineForScreen(index);
702 if (m_pMainFrame->m_bWrapLines)
704 int subLine = GetSubLineOffset(index);
705 if (subLine >= 0)
707 if (subLine < CountMultiLines(viewLine))
709 return m_ScreenedViewLine[viewLine].SubLines[subLine];
711 return L"";
714 return m_pViewData->GetLine(viewLine);
717 void CBaseView::CheckOtherView()
719 if (m_bOtherDiffChecked)
720 return;
721 // find out what the 'other' file is
722 m_pOtherViewData = nullptr;
723 m_pOtherView = nullptr;
724 if (this == m_pwndLeft && IsRightViewGood())
726 m_pOtherViewData = m_pwndRight->m_pViewData;
727 m_pOtherView = m_pwndRight;
730 if (this == m_pwndRight && IsLeftViewGood())
732 m_pOtherViewData = m_pwndLeft->m_pViewData;
733 m_pOtherView = m_pwndLeft;
736 m_bOtherDiffChecked = true;
740 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
742 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
743 ASSERT(viewData);
745 DiffStates origstate = viewData->GetState(nLineIndex);
747 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
748 nStartBlock = nLineIndex;
749 nEndBlock = nLineIndex;
750 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
752 DiffStates state = viewData->GetState(nStartBlock - 1);
753 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
754 origstate = state;
755 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
756 nStartBlock--;
757 else
758 break;
760 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
762 DiffStates state = viewData->GetState(nEndBlock + 1);
763 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
764 origstate = state;
765 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
766 nEndBlock++;
767 else
768 break;
772 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
774 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
776 int len = 0;
777 for (int i = nStartBlock; i <= nEndBlock; ++i)
778 len += viewData->GetLine(i).GetLength()+2;
780 CString block;
781 // do not check for whitespace blocks if the line is too long, because
782 // reserving a lot of memory here takes too much time (performance hog)
783 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
784 return block;
785 block.Preallocate(len+1);
786 for (int i = nStartBlock; i <= nEndBlock; ++i)
788 block += viewData->GetLine(i);
789 block += m_Eols[viewData->GetLineEnding(i)];
791 return block;
794 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
796 if (!m_pViewData)
797 return false;
798 bIdentical = false;
799 CheckOtherView();
800 if (!m_pOtherViewData)
801 return false;
802 int viewLine = GetViewLineForScreen(nLineIndex);
803 if (
804 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
805 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
808 bIdentical = true;
809 return false;
811 // first check whether the line itself only has whitespace changes
812 CString mine = m_pViewData->GetLine(viewLine);
813 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
814 if (mine.IsEmpty() && other.IsEmpty())
816 bIdentical = true;
817 return false;
820 if (mine == other)
822 bIdentical = true;
823 return true;
825 FilterWhitespaces(mine, other);
826 if (mine == other)
827 return true;
829 int nStartBlock2, nEndBlock2;
830 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
831 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
832 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
833 if (mine.IsEmpty())
834 bIdentical = false;
835 else
837 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
838 bIdentical = mine == other;
839 FilterWhitespaces(mine, other);
842 return (!mine.IsEmpty()) && (mine == other);
845 bool CBaseView::IsViewLineHidden(int nViewLine)
847 return IsViewLineHidden(m_pViewData, nViewLine);
850 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
852 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
855 int CBaseView::GetLineNumber(int index) const
857 if (!m_pViewData)
858 return -1;
859 int viewLine = GetViewLineForScreen(index);
860 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
861 return -1;
862 return m_pViewData->GetLineNumber(viewLine);
865 int CBaseView::GetScreenLines()
867 if (m_nScreenLines == -1)
869 CRect rect;
870 GetClientRect(&rect);
871 SCROLLBARINFO sbi = { sizeof(sbi) };
872 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
874 // only use the scroll bar size if the info is correct and the scrollbar is visible
875 // if anything isn't proper, assume the scrollbar has a size of zero
876 // and calculate the screen lines without it.
877 if (!(sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) && !(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
879 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
880 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
881 if (m_nScreenLines < 0)
882 m_nScreenLines = 0;
883 return m_nScreenLines;
886 // if the scroll bar is not visible, unavailable or there was an error,
887 // assume the scroll bar height is zero and return the screen lines here.
888 // Of course, that means the cache (using the member variable) won't work
889 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
890 return (rect.Height() - HEADERHEIGHT) / GetLineHeight();
892 return m_nScreenLines;
895 int CBaseView::GetAllMinScreenLines() const
897 int nLines = INT_MAX;
898 if (IsLeftViewGood())
899 nLines = m_pwndLeft->GetScreenLines();
900 if (IsRightViewGood())
901 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
902 if (IsBottomViewGood())
903 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
904 return (nLines == INT_MAX) || (nLines < 0) ? 0 : nLines;
907 int CBaseView::GetAllLineCount() const
909 int nLines = 0;
910 if (IsLeftViewGood())
911 nLines = m_pwndLeft->GetLineCount();
912 if (IsRightViewGood())
913 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
914 if (IsBottomViewGood())
915 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
916 return nLines;
919 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
921 if (IsLeftViewGood())
922 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
923 if (IsRightViewGood())
924 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
925 if (IsBottomViewGood())
926 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
929 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
931 SCROLLINFO si;
932 si.cbSize = sizeof(si);
933 if (bPositionOnly)
935 si.fMask = SIF_POS;
936 si.nPos = m_nTopLine;
938 else
940 EnableScrollBarCtrl(SB_VERT, TRUE);
941 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
943 m_nTopLine = 0;
944 Invalidate();
946 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
947 si.nMin = 0;
948 si.nMax = GetAllLineCount();
949 si.nPage = GetAllMinScreenLines();
950 si.nPos = m_nTopLine;
952 VERIFY(SetScrollInfo(SB_VERT, &si));
955 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
957 CView::OnVScroll(nSBCode, nPos, pScrollBar);
958 if (m_pwndLeft)
959 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
960 if (m_pwndRight)
961 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
962 if (m_pwndBottom)
963 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
964 if (m_pwndLocator)
965 m_pwndLocator->Invalidate();
968 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
970 // Note we cannot use nPos because of its 16-bit nature
971 SCROLLINFO si;
972 si.cbSize = sizeof(si);
973 si.fMask = SIF_ALL;
974 VERIFY(master->GetScrollInfo(SB_VERT, &si));
976 int nPageLines = GetScreenLines();
977 int nLineCount = GetLineCount();
979 int nNewTopLine;
981 static LONG textwidth = 0;
982 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
983 switch (nSBCode)
985 case SB_TOP:
986 nNewTopLine = 0;
987 break;
988 case SB_BOTTOM:
989 nNewTopLine = nLineCount - nPageLines + 1;
990 break;
991 case SB_LINEUP:
992 nNewTopLine = m_nTopLine - 1;
993 break;
994 case SB_LINEDOWN:
995 nNewTopLine = m_nTopLine + 1;
996 break;
997 case SB_PAGEUP:
998 nNewTopLine = m_nTopLine - si.nPage + 1;
999 break;
1000 case SB_PAGEDOWN:
1001 nNewTopLine = m_nTopLine + si.nPage - 1;
1002 break;
1003 case SB_THUMBPOSITION:
1004 m_ScrollTool.Clear();
1005 nNewTopLine = si.nTrackPos;
1006 textwidth = 0;
1007 break;
1008 case SB_THUMBTRACK:
1009 nNewTopLine = si.nTrackPos;
1010 if (GetFocus() == this)
1012 RECT thumbrect;
1013 GetClientRect(&thumbrect);
1014 ClientToScreen(&thumbrect);
1016 POINT thumbpoint;
1017 thumbpoint.x = thumbrect.right;
1018 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
1019 m_ScrollTool.Init(&thumbpoint);
1020 if (textwidth == 0)
1022 CString sTemp = sFormat;
1023 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
1024 textwidth = m_ScrollTool.GetTextWidth(sTemp);
1026 thumbpoint.x -= textwidth;
1027 int line = GetLineNumber(nNewTopLine);
1028 if (line >= 0)
1029 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
1030 else
1031 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
1033 break;
1034 default:
1035 return;
1038 if (nNewTopLine < 0)
1039 nNewTopLine = 0;
1040 if (nNewTopLine >= nLineCount)
1041 nNewTopLine = nLineCount - 1;
1042 ScrollToLine(nNewTopLine);
1045 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
1047 if (IsLeftViewGood())
1048 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
1049 if (IsRightViewGood())
1050 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
1051 if (IsBottomViewGood())
1052 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
1055 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
1057 SCROLLINFO si;
1058 si.cbSize = sizeof(si);
1059 if (bPositionOnly)
1061 si.fMask = SIF_POS;
1062 si.nPos = m_nOffsetChar;
1064 else
1066 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
1067 if (!m_pMainFrame->m_bWrapLines)
1069 int minScreenChars = GetAllMinScreenChars();
1070 int maxLineLength = GetAllMaxLineLength();
1071 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1073 m_nOffsetChar = 0;
1074 Invalidate();
1076 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1077 si.nMin = 0;
1078 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1079 si.nMax += GetMarginWidth()/GetCharWidth();
1080 si.nPage = GetScreenChars();
1081 si.nPos = m_nOffsetChar;
1084 VERIFY(SetScrollInfo(SB_HORZ, &si));
1087 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1089 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1090 if (m_pwndLeft)
1091 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1092 if (m_pwndRight)
1093 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1094 if (m_pwndBottom)
1095 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1096 if (m_pwndLocator)
1097 m_pwndLocator->Invalidate();
1100 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1102 SCROLLINFO si;
1103 si.cbSize = sizeof(si);
1104 si.fMask = SIF_ALL;
1105 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1107 int nPageChars = GetScreenChars();
1108 int nMaxLineLength = GetMaxLineLength();
1110 int nNewOffset;
1111 switch (nSBCode)
1113 case SB_LEFT:
1114 nNewOffset = 0;
1115 break;
1116 case SB_BOTTOM:
1117 nNewOffset = nMaxLineLength - nPageChars + 1;
1118 break;
1119 case SB_LINEUP:
1120 nNewOffset = m_nOffsetChar - 1;
1121 break;
1122 case SB_LINEDOWN:
1123 nNewOffset = m_nOffsetChar + 1;
1124 break;
1125 case SB_PAGEUP:
1126 nNewOffset = m_nOffsetChar - si.nPage + 1;
1127 break;
1128 case SB_PAGEDOWN:
1129 nNewOffset = m_nOffsetChar + si.nPage - 1;
1130 break;
1131 case SB_THUMBPOSITION:
1132 case SB_THUMBTRACK:
1133 nNewOffset = si.nTrackPos;
1134 break;
1135 default:
1136 return;
1139 if (nNewOffset >= nMaxLineLength)
1140 nNewOffset = nMaxLineLength - 1;
1141 if (nNewOffset < 0)
1142 nNewOffset = 0;
1143 ScrollToChar(nNewOffset, TRUE);
1146 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1148 if (m_nOffsetChar != nNewOffsetChar)
1150 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1151 m_nOffsetChar = nNewOffsetChar;
1152 CRect rcScroll;
1153 GetClientRect(&rcScroll);
1154 rcScroll.left += GetMarginWidth();
1155 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1156 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1157 // update the view header
1158 rcScroll.left = 0;
1159 rcScroll.top = 0;
1160 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1161 InvalidateRect(&rcScroll, FALSE);
1162 UpdateWindow();
1163 if (bTrackScrollBar)
1164 RecalcHorzScrollBar(TRUE);
1165 UpdateCaret();
1166 if (m_pwndLineDiffBar)
1167 m_pwndLineDiffBar->Invalidate();
1171 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1173 if (m_pwndLeft)
1174 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1175 if (m_pwndRight)
1176 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1177 if (m_pwndBottom)
1178 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1181 void CBaseView::ScrollAllSide(int delta)
1183 int nNewOffset = m_nOffsetChar;
1184 nNewOffset += delta;
1185 int nMaxLineLength = GetMaxLineLength();
1186 if (nNewOffset >= nMaxLineLength)
1187 nNewOffset = nMaxLineLength - 1;
1188 if (nNewOffset < 0)
1189 nNewOffset = 0;
1190 ScrollAllToChar(nNewOffset, TRUE);
1191 if (m_pwndLineDiffBar)
1192 m_pwndLineDiffBar->Invalidate();
1193 UpdateCaret();
1196 void CBaseView::ScrollSide(int delta)
1198 int nNewOffset = m_nOffsetChar;
1199 nNewOffset += delta;
1200 int nMaxLineLength = GetMaxLineLength();
1201 if (nNewOffset >= nMaxLineLength)
1202 nNewOffset = nMaxLineLength - 1;
1203 if (nNewOffset < 0)
1204 nNewOffset = 0;
1205 ScrollToChar(nNewOffset, TRUE);
1206 if (m_pwndLineDiffBar)
1207 m_pwndLineDiffBar->Invalidate();
1208 UpdateCaret();
1211 void CBaseView::ScrollVertical(short zDelta)
1213 const int nLineCount = GetLineCount();
1214 int nTopLine = m_nTopLine;
1215 nTopLine -= (zDelta/30);
1216 if (nTopLine < 0)
1217 nTopLine = 0;
1218 if (nTopLine >= nLineCount)
1219 nTopLine = nLineCount - 1;
1220 ScrollToLine(nTopLine, TRUE);
1223 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1225 if (m_nTopLine != nNewTopLine)
1227 if (nNewTopLine < 0)
1228 nNewTopLine = 0;
1230 int nScrollLines = m_nTopLine - nNewTopLine;
1232 m_nTopLine = nNewTopLine;
1233 CRect rcScroll;
1234 GetClientRect(&rcScroll);
1235 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1236 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1237 UpdateWindow();
1238 if (bTrackScrollBar)
1239 RecalcVertScrollBar(TRUE);
1240 UpdateCaret();
1245 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1247 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1249 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1251 int nViewLine = GetViewLineForScreen(nLineIndex);
1252 HICON icon = nullptr;
1253 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1254 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1255 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1257 DiffStates state = m_pViewData->GetState(nViewLine);
1258 switch (state)
1260 case DIFFSTATE_ADDED:
1261 case DIFFSTATE_THEIRSADDED:
1262 case DIFFSTATE_YOURSADDED:
1263 case DIFFSTATE_IDENTICALADDED:
1264 case DIFFSTATE_CONFLICTADDED:
1265 eIcon = TScreenedViewLine::ICN_ADD;
1266 break;
1267 case DIFFSTATE_REMOVED:
1268 case DIFFSTATE_THEIRSREMOVED:
1269 case DIFFSTATE_YOURSREMOVED:
1270 case DIFFSTATE_IDENTICALREMOVED:
1271 eIcon = TScreenedViewLine::ICN_REMOVED;
1272 break;
1273 case DIFFSTATE_CONFLICTED:
1274 eIcon = TScreenedViewLine::ICN_CONFLICT;
1275 break;
1276 case DIFFSTATE_CONFLICTED_IGNORED:
1277 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1278 break;
1279 case DIFFSTATE_EDITED:
1280 eIcon = TScreenedViewLine::ICN_EDIT;
1281 break;
1282 default:
1283 break;
1285 bool bIdentical = false;
1286 int blockstart = -1;
1287 int blockend = -1;
1288 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1290 if (bIdentical)
1291 eIcon = TScreenedViewLine::ICN_SAME;
1292 else
1293 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1294 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1296 if (nViewLine > blockstart)
1297 Invalidate(); // redraw the upper icons since they're now changing
1298 while (blockstart <= blockend)
1299 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1302 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1303 eIcon = TScreenedViewLine::ICN_MOVED;
1304 if (m_pViewData->GetMarked(nViewLine))
1305 eIcon = TScreenedViewLine::ICN_MARKED;
1306 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1308 switch (eIcon)
1310 case TScreenedViewLine::ICN_UNKNOWN:
1311 case TScreenedViewLine::ICN_NONE:
1312 break;
1313 case TScreenedViewLine::ICN_SAME:
1314 icon = m_hEqualIcon;
1315 break;
1316 case TScreenedViewLine::ICN_EDIT:
1317 icon = m_hEditedIcon;
1318 break;
1319 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1320 icon = m_hWhitespaceBlockIcon;
1321 break;
1322 case TScreenedViewLine::ICN_ADD:
1323 icon = m_hAddedIcon;
1324 break;
1325 case TScreenedViewLine::ICN_CONFLICT:
1326 icon = m_hConflictedIcon;
1327 break;
1328 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1329 icon = m_hConflictedIgnoredIcon;
1330 break;
1331 case TScreenedViewLine::ICN_REMOVED:
1332 icon = m_hRemovedIcon;
1333 break;
1334 case TScreenedViewLine::ICN_MOVED:
1335 icon = m_hMovedIcon;
1336 break;
1337 case TScreenedViewLine::ICN_MARKED:
1338 icon = m_hMarkedIcon;
1339 break;
1342 int iconWidth = GetSystemMetrics(SM_CXSMICON);
1343 int iconHeight = GetSystemMetrics(SM_CYSMICON);
1344 if (icon)
1346 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height() - iconHeight) / 2, icon, iconWidth, iconHeight, 0, nullptr, DI_NORMAL);
1348 if ((m_bViewLinenumbers)&&(m_nDigits))
1350 int nSubLine = GetSubLineOffset(nLineIndex);
1351 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1352 CString sLinenumber;
1353 if (bIsFirstSubline)
1355 CString sLinenumberFormat;
1356 int nLineNumber = GetLineNumber(nLineIndex);
1357 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1359 // TODO: do not show if there is no number hidden
1360 // TODO: show number if there is only one
1361 sLinenumberFormat.Format(L"%%%ds", m_nDigits);
1362 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? L"↕⁞" : L"⁞"); // alternative …
1364 else if (nLineNumber >= 0)
1366 sLinenumberFormat.Format(L"%%%dd", m_nDigits);
1367 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1369 else if (m_pMainFrame->m_bWrapLines)
1371 sLinenumberFormat.Format(L"%%%ds", m_nDigits);
1372 sLinenumber.Format(sLinenumberFormat, L"·");
1374 if (!sLinenumber.IsEmpty())
1376 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1377 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1379 pdc->SelectObject(GetFont());
1380 pdc->ExtTextOut(rect.left + iconWidth + 2, rect.top, ETO_CLIPPED, &rect, sLinenumber, nullptr);
1387 int CBaseView::GetMarginWidth()
1389 int marginWidth = GetSystemMetrics(SM_CXSMICON) + 2 + 2;
1391 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1393 if (m_nDigits <= 0)
1395 int nLength = (int)m_pViewData->GetCount();
1396 // find out how many digits are needed to show the highest line number
1397 CString sMax;
1398 sMax.Format(L"%d", nLength);
1399 m_nDigits = sMax.GetLength();
1401 int nWidth = GetCharWidth();
1402 marginWidth += (m_nDigits * nWidth) + 2;
1405 return marginWidth;
1408 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1410 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1411 COLORREF crBk, crFg;
1412 if (IsBottomViewGood())
1414 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1415 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1417 else
1419 DiffStates state = DIFFSTATE_REMOVED;
1420 if (this == m_pwndRight)
1422 state = DIFFSTATE_ADDED;
1424 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1426 pdc->SetBkColor(crBk);
1427 pdc->FillSolidRect(textrect, crBk);
1429 pdc->SetTextColor(crFg);
1431 pdc->SelectObject(GetFont(FALSE, TRUE));
1433 CString sViewTitle;
1434 if (IsModified())
1436 sViewTitle = L"* " + m_sWindowName;
1438 else
1440 sViewTitle = m_sWindowName;
1442 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1443 if (nStringLength > rect.Width())
1445 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1446 sViewTitle = m_sWindowName.Mid(offset);
1448 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1449 rect.top + (HEADERHEIGHT / 2), ETO_CLIPPED, textrect, sViewTitle, nullptr);
1450 if (this->GetFocus() == this)
1451 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1452 else
1453 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1456 void CBaseView::OnDraw(CDC * pDC)
1458 CRect rcClient;
1459 GetClientRect(rcClient);
1461 int nLineCount = GetLineCount();
1462 int nLineHeight = GetLineHeight();
1464 CDC cacheDC;
1465 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1466 if (!m_pCacheBitmap)
1468 m_pCacheBitmap = new CBitmap;
1469 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1471 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1473 DrawHeader(pDC, rcClient);
1475 CRect rcLine;
1476 rcLine = rcClient;
1477 rcLine.top += nLineHeight+HEADERHEIGHT;
1478 rcLine.bottom = rcLine.top + nLineHeight;
1479 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1480 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1482 int nCurrentLine = m_nTopLine;
1483 bool bBeyondFileLineCached = false;
1484 while (rcLine.top < rcClient.bottom)
1486 if (nCurrentLine < nLineCount)
1488 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1489 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1490 bBeyondFileLineCached = false;
1492 else if (!bBeyondFileLineCached)
1494 DrawMargin(&cacheDC, rcCacheMargin, -1);
1495 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1496 bBeyondFileLineCached = true;
1499 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1501 nCurrentLine ++;
1502 rcLine.OffsetRect(0, nLineHeight);
1505 cacheDC.SelectObject(pOldBitmap);
1506 cacheDC.DeleteDC();
1509 bool CBaseView::IsStateConflicted(DiffStates state)
1511 switch (state)
1513 case DIFFSTATE_CONFLICTED:
1514 case DIFFSTATE_CONFLICTED_IGNORED:
1515 case DIFFSTATE_CONFLICTEMPTY:
1516 case DIFFSTATE_CONFLICTADDED:
1517 return true;
1519 return false;
1522 bool CBaseView::IsStateEmpty(DiffStates state)
1524 switch (state)
1526 case DIFFSTATE_CONFLICTEMPTY:
1527 case DIFFSTATE_UNKNOWN:
1528 case DIFFSTATE_EMPTY:
1529 return true;
1531 return false;
1534 bool CBaseView::IsStateRemoved(DiffStates state)
1536 switch (state)
1538 case DIFFSTATE_REMOVED:
1539 case DIFFSTATE_THEIRSREMOVED:
1540 case DIFFSTATE_YOURSREMOVED:
1541 case DIFFSTATE_IDENTICALREMOVED:
1542 return true;
1544 return false;
1547 DiffStates CBaseView::ResolveState(DiffStates state)
1549 if (IsStateConflicted(state))
1551 if (state == DIFFSTATE_CONFLICTEMPTY)
1552 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1553 else
1554 return DIFFSTATE_CONFLICTRESOLVED;
1556 return state;
1560 bool CBaseView::IsLineEmpty(int nLineIndex)
1562 if (m_pViewData == 0)
1563 return FALSE;
1564 int nViewLine = GetViewLineForScreen(nLineIndex);
1565 return IsViewLineEmpty(nViewLine);
1568 bool CBaseView::IsViewLineEmpty(int nViewLine)
1570 if (m_pViewData == 0)
1571 return FALSE;
1572 const DiffStates state = m_pViewData->GetState(nViewLine);
1573 return IsStateEmpty(state);
1576 bool CBaseView::IsLineRemoved(int nLineIndex)
1578 if (m_pViewData == 0)
1579 return FALSE;
1580 int nViewLine = GetViewLineForScreen(nLineIndex);
1581 return IsViewLineRemoved(nViewLine);
1584 bool CBaseView::IsViewLineRemoved(int nViewLine)
1586 if (m_pViewData == 0)
1587 return FALSE;
1588 const DiffStates state = m_pViewData->GetState(nViewLine);
1589 return IsStateRemoved(state);
1592 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1594 if (m_pViewData == 0)
1595 return false;
1596 const DiffStates state = m_pViewData->GetState(nLineIndex);
1597 return IsStateConflicted(state);
1600 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1602 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1605 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1607 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1610 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1612 if (origin.x < (rc.left - GetCharWidth() +1))
1613 return;
1614 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1615 return;
1616 int viewLine = GetViewLineForScreen(nLineIndex);
1617 EOL ending = m_pViewData->GetLineEnding(viewLine);
1618 if (m_bIconLFs)
1620 HICON hEndingIcon = nullptr;
1621 switch (ending)
1623 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1624 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1625 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1626 default: return;
1628 // If EOL style has changed, color end-of-line markers as inline differences.
1630 m_bShowInlineDiff && m_pOtherViewData &&
1631 (viewLine < m_pOtherViewData->GetCount()) &&
1632 (ending != EOL_NOENDING) &&
1633 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1634 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1637 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1640 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), 0, nullptr, DI_NORMAL);
1642 else
1644 CDpiScale dpi(pDC->GetSafeHdc());
1645 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1646 CPen * oldpen = pDC->SelectObject(&pen);
1647 int yMiddle = origin.y + rc.Height()/2;
1648 int xMiddle = origin.x+GetCharWidth()/2;
1649 bool bMultiline = false;
1650 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1652 if (GetLineLength(nLineIndex+1))
1654 // multiline
1655 bMultiline = true;
1656 pDC->MoveTo(origin.x, yMiddle-dpi.ScaleY(2));
1657 pDC->LineTo(origin.x + GetCharWidth() - dpi.ScaleX(1), yMiddle - dpi.ScaleY(2));
1658 pDC->LineTo(origin.x + GetCharWidth() - dpi.ScaleX(1), yMiddle + dpi.ScaleY(2));
1659 pDC->LineTo(origin.x, yMiddle + dpi.ScaleY(2));
1661 else if (GetLineLength(nLineIndex) == 0)
1662 bMultiline = true;
1664 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1665 bMultiline = true;
1667 if (!bMultiline)
1669 switch (ending)
1671 case EOL_AUTOLINE:
1672 case EOL_CRLF:
1673 // arrow from top to middle+2, then left
1674 pDC->MoveTo(origin.x + GetCharWidth() - dpi.ScaleX(1), rc.top+dpi.ScaleY(1));
1675 pDC->LineTo(origin.x + GetCharWidth() - dpi.ScaleX(1), yMiddle);
1676 case EOL_CR:
1677 // arrow from right to left
1678 pDC->MoveTo(origin.x + GetCharWidth() - dpi.ScaleX(1), yMiddle);
1679 pDC->LineTo(origin.x, yMiddle);
1680 pDC->LineTo(origin.x + dpi.ScaleX(4), yMiddle + dpi.ScaleY(4));
1681 pDC->MoveTo(origin.x, yMiddle);
1682 pDC->LineTo(origin.x + dpi.ScaleX(4), yMiddle - dpi.ScaleY(4));
1683 break;
1684 case EOL_LFCR:
1685 // from right-upper to left then down
1686 pDC->MoveTo(origin.x + GetCharWidth() - dpi.ScaleX(1), yMiddle-dpi.ScaleY(2));
1687 pDC->LineTo(xMiddle, yMiddle - dpi.ScaleY(2));
1688 pDC->LineTo(xMiddle, rc.bottom - dpi.ScaleY(1));
1689 pDC->LineTo(xMiddle + dpi.ScaleX(4), rc.bottom - dpi.ScaleY(5));
1690 pDC->MoveTo(xMiddle, rc.bottom - dpi.ScaleY(1));
1691 pDC->LineTo(xMiddle - dpi.ScaleX(4), rc.bottom - dpi.ScaleY(5));
1692 break;
1693 case EOL_LF:
1694 // arrow from top to bottom
1695 pDC->MoveTo(xMiddle, rc.top);
1696 pDC->LineTo(xMiddle, rc.bottom - dpi.ScaleY(1));
1697 pDC->LineTo(xMiddle + dpi.ScaleX(4), rc.bottom - dpi.ScaleY(5));
1698 pDC->MoveTo(xMiddle, rc.bottom - dpi.ScaleY(1));
1699 pDC->LineTo(xMiddle - dpi.ScaleX(4), rc.bottom - dpi.ScaleY(5));
1700 break;
1701 case EOL_FF: // Form Feed, U+000C
1702 case EOL_NEL: // Next Line, U+0085
1703 case EOL_LS: // Line Separator, U+2028
1704 case EOL_PS: // Paragraph Separator, U+2029
1705 // draw a horizontal line at the bottom of this line
1706 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1707 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1708 pDC->LineTo(origin.x, rc.bottom-2);
1709 pDC->LineTo(origin.x+5, rc.bottom-2);
1710 pDC->MoveTo(origin.x, rc.bottom-2);
1711 pDC->LineTo(origin.x+1, rc.bottom-6);
1712 break;
1713 default: // other EOLs
1714 // arrow from top right to bottom left
1715 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1716 pDC->LineTo(origin.x, rc.bottom-1);
1717 pDC->LineTo(origin.x+5, rc.bottom-2);
1718 pDC->MoveTo(origin.x, rc.bottom-1);
1719 pDC->LineTo(origin.x+1, rc.bottom-6);
1720 break;
1721 case EOL_NOENDING:
1722 break;
1725 pDC->SelectObject(oldpen);
1729 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1731 if (!m_bShowSelection)
1732 return;
1734 int nSelBlockStart;
1735 int nSelBlockEnd;
1736 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1737 return;
1739 const int THICKNESS = 2;
1740 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1742 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1743 int nSubLine = GetSubLineOffset(nLineIndex);
1744 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1745 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1747 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1750 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1751 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1753 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1757 void CBaseView::DrawTextLine(
1758 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1760 ASSERT(nLineIndex < GetLineCount());
1761 int nViewLine = GetViewLineForScreen(nLineIndex);
1762 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1764 LineColors lineCols = GetLineColors(nViewLine);
1766 CString sViewLine = GetViewLineChars(nViewLine);
1767 // mark selection
1768 if (m_bShowSelection && HasTextSelection())
1770 // has this line selection ?
1771 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1773 int nViewLineLength = sViewLine.GetLength();
1775 // first suppose the whole line is selected
1776 int selectedStart = 0;
1777 int selectedEnd = nViewLineLength;
1779 // the view line is partially selected
1780 if (m_ptSelectionViewPosStart.y == nViewLine)
1782 selectedStart = m_ptSelectionViewPosStart.x;
1785 if (m_ptSelectionViewPosEnd.y == nViewLine)
1787 selectedEnd = m_ptSelectionViewPosEnd.x;
1789 // apply selection coloring
1790 // First enforce start and end point
1791 lineCols.SplitBlock(selectedStart);
1792 lineCols.SplitBlock(selectedEnd);
1793 // change color of affected parts
1794 long intenseColorScale = m_bFocused ? 70 : 30;
1795 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1796 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1798 auto& second = it->second;
1799 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, second.background);
1800 if (second.shot == second.background)
1801 second.shot = crBk;
1802 second.background = crBk;
1803 second.text = CAppUtils::IntenseColor(intenseColorScale, second.text);
1808 // TODO: remove duplicate from selection and mark
1809 if (!m_sMarkedWord.IsEmpty())
1811 int nMarkLength = m_sMarkedWord.GetLength();
1812 //int nViewLineLength = sViewLine.GetLength();
1813 const TCHAR * text = sViewLine;
1814 const TCHAR * findText = text;
1815 while ((findText = wcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1817 int nMarkStart = static_cast<int>(findText - text);
1818 int nMarkEnd = nMarkStart + nMarkLength;
1819 findText += nMarkLength;
1820 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1821 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1822 if (eLeft == eStart)
1823 continue;
1824 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1825 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1826 if (eRight == eEnd)
1827 continue;
1829 // First enforce start and end point
1830 lineCols.SplitBlock(nMarkStart);
1831 lineCols.SplitBlock(nMarkEnd);
1832 // change color of affected parts
1833 const long int nIntenseColorScale = 200;
1834 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1835 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1837 auto& second = it->second;
1838 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1839 if (second.shot == second.background)
1840 second.shot = crBk;
1841 second.background = crBk;
1842 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1846 if (!m_sFindText.IsEmpty())
1848 int nMarkStart = 0;
1849 int nMarkEnd = 0;
1850 int nStringPos = nMarkStart;
1851 CString searchLine = sViewLine;
1852 if (!m_bMatchCase)
1853 searchLine.MakeLower();
1854 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1856 // First enforce start and end point
1857 lineCols.SplitBlock(nMarkStart+nStringPos);
1858 lineCols.SplitBlock(nMarkEnd+nStringPos);
1859 // change color of affected parts
1860 const long int nIntenseColorScale = 30;
1861 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1862 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1864 auto& second = it->second;
1865 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1866 if (second.shot == second.background)
1867 second.shot = crBk;
1868 second.background = crBk;
1869 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1871 searchLine = searchLine.Mid(nMarkEnd);
1872 nStringPos = nMarkEnd;
1873 nMarkStart = 0;
1874 nMarkEnd = 0;
1878 // @ this point we may cache data for next line which may be same in wrapped mode
1880 int nTextOffset = 0;
1881 int nSubline = GetSubLineOffset(nLineIndex);
1882 for (int n=0; n<nSubline; n++)
1884 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1885 nTextOffset += sLine.GetLength();
1888 CString sLine = GetLineChars(nLineIndex);
1889 int nLineLength = sLine.GetLength();
1890 CString sLineExp = ExpandChars(sLine);
1891 LPCTSTR textExp = sLineExp;
1892 //int nLineLengthExp = sLineExp.GetLength();
1893 int nStartExp = 0;
1894 int nLeft = coords.x;
1895 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1897 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1898 ++itEnd;
1899 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1900 int nEnd = nLineLength;
1901 if (itEnd != lineCols.end())
1903 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1905 int nBlockLength = nEnd - nStart;
1906 if (nBlockLength > 0 && nEnd>=0)
1908 auto& second = itStart->second;
1909 pDC->SetBkColor(second.background);
1910 pDC->SetTextColor(second.text);
1911 int nEndExp = CountExpandedChars(sLine, nEnd);
1912 int nTextLength = nEndExp - nStartExp;
1913 LPCTSTR p_zBlockText = textExp + nStartExp;
1914 SIZE Size;
1915 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1916 int nRight = nLeft + Size.cx;
1917 if ((nRight > rc.left) && (nLeft < rc.right))
1919 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1920 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1921 // is 4094 (4095 doesn't work anymore).
1922 // So we limit the length here to that 4094 chars.
1923 // In case we're scrolled to the right, there's no need to draw the string
1924 // from way outside our window, so we also offset the drawing to the start of the window.
1925 // This reduces the string length as well.
1926 int offset = 0;
1927 int leftcoord = nLeft;
1928 if (nLeft < 0)
1930 int fit = nTextLength;
1931 auto posBuffer = std::make_unique<int[]>(fit);
1932 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1933 int lower = 0, upper = fit - 1;
1936 int middle = (upper + lower + 1) / 2;
1937 int width = posBuffer[middle];
1938 if (rc.left - nLeft < width)
1939 upper = middle - 1;
1940 else
1941 lower = middle;
1942 } while (lower < upper);
1944 offset = lower;
1945 nTextLength -= offset;
1946 leftcoord += lower > 0 ? posBuffer[lower - 1] : 0;
1949 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText + offset, min(nTextLength, 4094), nullptr);
1950 if ((second.shot != second.background) && (itStart->first == nStart + nTextOffset))
1951 pDC->FillSolidRect(nLeft - 1, rc.top, 1, rc.Height(), second.shot);
1953 nLeft = nRight;
1954 coords.x = nRight;
1955 nStartExp = nEndExp;
1960 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1962 if (nLineIndex >= GetLineCount())
1963 nLineIndex = -1;
1964 ASSERT(nLineIndex >= -1);
1966 if ((nLineIndex == -1) || !m_pViewData)
1968 // Draw line beyond the text
1969 COLORREF crBkgnd, crText;
1970 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1971 pDC->FillSolidRect(rc, crBkgnd);
1972 return;
1975 int viewLine = GetViewLineForScreen(nLineIndex);
1976 if (m_pMainFrame->m_bCollapsed)
1978 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1980 COLORREF crBkgnd, crText;
1981 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1982 pDC->FillSolidRect(rc, crBkgnd);
1984 const int THICKNESS = 2;
1985 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1986 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1987 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1988 pDC->SetBkColor(crBkgnd);
1989 CRect rect = rc;
1990 pDC->DrawText(L"{...}", &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1991 return;
1995 DiffStates diffState = m_pViewData->GetState(viewLine);
1996 COLORREF crBkgnd, crText;
1997 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1999 if (diffState == DIFFSTATE_CONFLICTED)
2001 // conflicted lines are shown without 'text' on them
2002 CRect rect = rc;
2003 pDC->FillSolidRect(rc, crBkgnd);
2004 // now draw some faint text patterns
2005 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
2006 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
2007 DrawBlockLine(pDC, rc, nLineIndex);
2008 return;
2011 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
2012 CString sLine = GetLineChars(nLineIndex);
2013 if (sLine.IsEmpty())
2015 pDC->FillSolidRect(rc, crBkgnd);
2016 DrawBlockLine(pDC, rc, nLineIndex);
2017 DrawLineEnding(pDC, rc, nLineIndex, origin);
2018 return;
2021 CheckOtherView();
2023 // Draw the line
2025 pDC->SelectObject(GetFont(FALSE, FALSE));
2027 DrawTextLine(pDC, rc, nLineIndex, origin);
2029 // draw white space after the end of line
2030 CRect frect = rc;
2031 if (origin.x > frect.left)
2032 frect.left = origin.x;
2033 if (frect.right > frect.left)
2034 pDC->FillSolidRect(frect, crBkgnd);
2036 // draw the whitespace chars
2037 LPCTSTR pszChars = (LPCWSTR)sLine;
2038 if (m_bViewWhitespace)
2040 int xpos = 0;
2041 int nChars = 0;
2042 LPCTSTR pLastSpace = pszChars;
2043 int y = rc.top + (rc.bottom-rc.top)/2;
2044 xpos -= m_nOffsetChar * GetCharWidth();
2046 CDpiScale dpi(pDC->GetSafeHdc());
2047 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2048 while (*pszChars)
2050 switch (*pszChars)
2052 case '\t':
2054 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2055 pLastSpace = pszChars + 1;
2056 // draw an arrow
2057 int nSpaces = GetTabSize() - nChars % GetTabSize();
2058 if (xpos + nSpaces * GetCharWidth() > 0)
2060 int xposreal = max(xpos, 0);
2061 if ((xposreal > 0) || (nSpaces > 0))
2063 CPen * oldPen = pDC->SelectObject(&pen);
2064 pDC->MoveTo(xposreal + rc.left + dpi.ScaleX(2), y);
2065 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - dpi.ScaleX(2), y);
2066 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - dpi.ScaleX(6), y - dpi.ScaleY(4));
2067 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - dpi.ScaleX(2), y);
2068 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - dpi.ScaleX(6), y + dpi.ScaleY(4));
2069 pDC->SelectObject(oldPen);
2072 xpos += nSpaces * GetCharWidth();
2073 nChars += nSpaces;
2075 break;
2076 case ' ':
2078 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2079 pLastSpace = pszChars + 1;
2080 if (xpos >= 0)
2082 const int cxWhitespace = dpi.ScaleX(2);
2083 const int cyWhitespace = dpi.ScaleY(2);
2084 // draw 2-logical pixel rectangle, like Scintilla editor.
2085 pDC->FillSolidRect(xpos + rc.left + GetCharWidth() / 2 - cxWhitespace/2, y, cxWhitespace, cyWhitespace, m_WhiteSpaceFg);
2087 xpos += GetCharWidth();
2088 nChars++;
2090 break;
2091 default:
2092 nChars++;
2093 break;
2095 pszChars++;
2098 DrawBlockLine(pDC, rc, nLineIndex);
2099 if (origin.x >= rc.left)
2100 DrawLineEnding(pDC, rc, nLineIndex, origin);
2103 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2105 if (nCount <= 0)
2107 line.Empty();
2108 return;
2111 int nTabSize = GetTabSize();
2113 int nActualOffset = CountExpandedChars(sLine, nOffset);
2115 LPCTSTR pszChars = (LPCWSTR)sLine;
2116 pszChars += nOffset;
2117 int nLength = nCount;
2119 int nTabCount = 0;
2120 for (int i=0; i<nLength; i++)
2122 if (pszChars[i] == L'\t')
2123 nTabCount ++;
2126 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2127 int nCurPos = 0;
2128 if (nTabCount > 0 || m_bViewWhitespace)
2130 for (int i=0; i<nLength; i++)
2132 if (pszChars[i] == L'\t')
2134 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2135 while (nSpaces > 0)
2137 pszBuf[nCurPos ++] = L' ';
2138 nSpaces --;
2141 else
2143 pszBuf[nCurPos] = pszChars[i];
2144 nCurPos ++;
2148 else
2150 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2151 nCurPos = nLength;
2153 pszBuf[nCurPos] = 0;
2154 line.ReleaseBuffer();
2157 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2159 CString sRet;
2160 int nLength = sLine.GetLength();
2161 ExpandChars(sLine, nOffset, nLength, sRet);
2162 return sRet;
2165 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2167 int nTabSize = GetTabSize();
2169 int nActualOffset = 0;
2170 for (int i=0; i<nLength; i++)
2172 if (sLine[i] == L'\t')
2173 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2174 else
2175 nActualOffset ++;
2177 return nActualOffset;
2180 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2182 if (m_pwndLeft)
2183 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2184 if (m_pwndRight)
2185 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2186 if (m_pwndBottom)
2187 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2188 if (m_pwndLocator)
2189 m_pwndLocator->Invalidate();
2192 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2194 //almost the same as ScrollAllToLine, but try to put the line in the
2195 //middle of the view, not on top
2196 int nNewTopLine = nNewLine - GetScreenLines()/2;
2197 if (nNewTopLine < 0)
2198 nNewTopLine = 0;
2199 if (nNewTopLine >= (int)m_Screen2View.size())
2200 nNewTopLine = (int)m_Screen2View.size()-1;
2201 if (bAll)
2202 ScrollAllToLine(nNewTopLine);
2203 else
2204 ScrollToLine(nNewTopLine);
2207 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2209 return TRUE;
2212 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2214 if (CView::OnCreate(lpCreateStruct) == -1)
2215 return -1;
2217 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2218 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2219 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2220 m_lfBaseFont.lfHeight = 0;
2221 m_lfBaseFont.lfWeight = FW_NORMAL;
2222 m_lfBaseFont.lfItalic = FALSE;
2223 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2224 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2225 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2226 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2227 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2229 return 0;
2232 void CBaseView::OnDestroy()
2234 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2236 m_pFindDialog->SendMessage(WM_CLOSE);
2237 return;
2239 CView::OnDestroy();
2240 DeleteFonts();
2241 ReleaseBitmap();
2244 void CBaseView::OnSize(UINT nType, int cx, int cy)
2246 CView::OnSize(nType, cx, cy);
2247 ReleaseBitmap();
2249 m_nScreenLines = -1;
2250 m_nScreenChars = -1;
2251 if (m_nLastScreenChars != GetScreenChars())
2253 BuildAllScreen2ViewVector();
2254 m_nLastScreenChars = m_nScreenChars;
2255 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2257 // if we're in wrap mode, the line wrapping most likely changed
2258 // and that means we have to redraw the whole window, not just the
2259 // scrolled part.
2260 Invalidate(FALSE);
2262 else
2264 // make sure the view header is redrawn
2265 CRect rcScroll;
2266 GetClientRect(&rcScroll);
2267 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2268 InvalidateRect(&rcScroll, FALSE);
2271 else
2273 // make sure the view header is redrawn
2274 CRect rcScroll;
2275 GetClientRect(&rcScroll);
2276 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2277 InvalidateRect(&rcScroll, FALSE);
2279 UpdateLocator();
2280 RecalcVertScrollBar();
2281 RecalcHorzScrollBar();
2283 UpdateCaret();
2286 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2288 if (m_pwndLeft)
2289 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2290 if (m_pwndRight)
2291 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2292 if (m_pwndBottom)
2293 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2294 if (m_pwndLocator)
2295 m_pwndLocator->Invalidate();
2296 return CView::OnMouseWheel(nFlags, zDelta, pt);
2299 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2301 if (m_pwndLeft)
2302 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2303 if (m_pwndRight)
2304 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2305 if (m_pwndBottom)
2306 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2307 if (m_pwndLocator)
2308 m_pwndLocator->Invalidate();
2311 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2313 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2314 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2316 if (bControl || bShift)
2318 if (m_pMainFrame->m_bWrapLines)
2319 return;
2320 // Ctrl-Wheel scrolls sideways
2321 ScrollSide(-zDelta/30);
2323 else
2325 ScrollVertical(zDelta);
2329 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2331 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2332 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2334 if (bControl || bShift)
2336 // Ctrl-H-Wheel scrolls vertical
2337 ScrollVertical(zDelta);
2339 else
2341 if (m_pMainFrame->m_bWrapLines)
2342 return;
2343 // Ctrl-Wheel scrolls sideways
2344 ScrollSide(zDelta/30);
2348 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2350 if (nHitTest == HTCLIENT)
2352 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2354 if (m_nMouseLine < (int)m_Screen2View.size())
2356 if (m_nMouseLine >= 0)
2358 int viewLine = GetViewLineForScreen(m_nMouseLine);
2359 if (viewLine < m_pViewData->GetCount())
2361 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2363 ::SetCursor(::LoadCursor(nullptr, IDC_HAND));
2364 return TRUE;
2370 if (m_mouseInMargin)
2372 ::SetCursor(m_margincursor);
2373 return TRUE;
2375 if (m_nMouseLine >= 0)
2377 ::SetCursor(::LoadCursor(nullptr, IDC_IBEAM)); // Set To Edit Cursor
2378 return TRUE;
2381 ::SetCursor(::LoadCursor(nullptr, IDC_ARROW)); // Set To Arrow Cursor
2382 return TRUE;
2384 return CView::OnSetCursor(pWnd, nHitTest, message);
2387 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2389 CView::OnKillFocus(pNewWnd);
2390 m_bFocused = FALSE;
2391 UpdateCaret();
2392 Invalidate();
2395 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2397 CView::OnSetFocus(pOldWnd);
2398 m_bFocused = TRUE;
2399 UpdateCaret();
2400 Invalidate();
2403 int CBaseView::GetLineFromPoint(CPoint point)
2405 ScreenToClient(&point);
2406 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2409 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2411 CRect rcClient;
2412 GetClientRect(rcClient);
2413 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight + HEADERHEIGHT);
2415 CRect borderrect(rcClient.left, rcClient.top + m_nLineHeight + HEADERHEIGHT, 0, rcClient.bottom);
2417 CPoint ptLocal = point;
2418 ScreenToClient(&ptLocal);
2420 if (textrect.PtInRect(ptLocal) || borderrect.PtInRect(ptLocal))
2422 // inside the header part of the view (showing the filename)
2423 if (IsViewGood(m_pwndBottom))
2425 CString temp;
2426 if (this == m_pwndLeft)
2428 CIconMenu popup;
2429 if (!popup.CreatePopupMenu())
2430 return;
2432 temp.LoadString(IDS_HEADER_DIFFLEFTTOBASE);
2433 popup.AppendMenu(MF_STRING | MF_ENABLED, 10, temp);
2434 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2435 if (cmd == 10)
2436 m_pMainFrame->DiffLeftToBase();
2438 if (this == m_pwndRight)
2440 CIconMenu popup;
2441 if (!popup.CreatePopupMenu())
2442 return;
2444 temp.LoadString(IDS_HEADER_DIFFRIGHTTOBASE);
2445 popup.AppendMenu(MF_STRING | MF_ENABLED, 10, temp);
2446 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2447 if (cmd == 10)
2448 m_pMainFrame->DiffRightToBase();
2451 return;
2454 if (!this->IsWindowVisible())
2455 return;
2457 CIconMenu popup;
2458 if (!popup.CreatePopupMenu())
2459 return;
2461 AddContextItems(popup, state);
2463 CMenu popupEols;
2464 CMenu popupUnicode;
2465 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2466 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2467 if (IsWritable())
2469 CString temp;
2470 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2471 temp.LoadString(IDS_EDIT_TAB2SPACE);
2472 popup.AppendMenu(MF_STRING | (oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_TABTOSPACES, temp);
2473 temp.LoadString(IDS_EDIT_SPACE2TAB);
2474 popup.AppendMenu(MF_STRING | (oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_SPACESTOTABS, temp);
2475 temp.LoadString(IDS_EDIT_TRIM);
2476 popup.AppendMenu(MF_STRING | (oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2478 // add eol submenu
2479 if (!popupEols.CreatePopupMenu())
2480 return;
2482 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2483 for (int i = 1; i < _countof(eolArray); i++)
2485 temp = GetEolName(eolArray[i]);
2486 bool bChecked = (eEolType == eolArray[i]);
2487 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2490 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2491 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2493 // add encoding submenu
2494 if (!popupUnicode.CreatePopupMenu())
2495 return;
2496 for (int i = 0; i < _countof(uctArray); i++)
2498 temp = CFileTextLines::GetEncodingName(uctArray[i]);
2499 bool bChecked = (m_texttype == uctArray[i]);
2500 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2502 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2503 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2507 CompensateForKeyboard(point);
2509 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2510 ResetUndoStep();
2511 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2513 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2515 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2517 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2518 SaveUndoStep();
2520 switch (cmd)
2522 // 2-pane view commands; target is right view
2523 case POPUPCOMMAND_USELEFTBLOCK:
2524 m_pwndRight->UseLeftBlock();
2525 break;
2526 case POPUPCOMMAND_USELEFTFILE:
2527 m_pwndRight->UseLeftFile();
2528 break;
2529 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2530 m_pwndRight->UseBothLeftFirst();
2531 break;
2532 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2533 m_pwndRight->UseBothRightFirst();
2534 break;
2535 case POPUPCOMMAND_MARKBLOCK:
2536 m_pwndRight->MarkBlock(true);
2537 break;
2538 case POPUPCOMMAND_UNMARKBLOCK:
2539 m_pwndRight->MarkBlock(false);
2540 break;
2541 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2542 m_pwndRight->LeaveOnlyMarkedBlocks();
2543 break;
2544 // 2-pane view multiedit commands; target is left view
2545 case POPUPCOMMAND_PREPENDFROMRIGHT:
2546 if (!m_pwndLeft->IsReadonly())
2547 m_pwndLeft->UseBothRightFirst();
2548 break;
2549 case POPUPCOMMAND_REPLACEBYRIGHT:
2550 if (!m_pwndLeft->IsReadonly())
2551 m_pwndLeft->UseRightBlock();
2552 break;
2553 case POPUPCOMMAND_APPENDFROMRIGHT:
2554 if (!m_pwndLeft->IsReadonly())
2555 m_pwndLeft->UseBothLeftFirst();
2556 break;
2557 case POPUPCOMMAND_USERIGHTFILE:
2558 m_pwndLeft->UseRightFile();
2559 break;
2560 // 3-pane view commands; target is bottom view
2561 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2562 m_pwndBottom->UseBothRightFirst();
2563 break;
2564 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2565 m_pwndBottom->UseBothLeftFirst();
2566 break;
2567 case POPUPCOMMAND_USEYOURBLOCK:
2568 m_pwndBottom->UseRightBlock();
2569 break;
2570 case POPUPCOMMAND_USEYOURFILE:
2571 m_pwndBottom->UseRightFile();
2572 break;
2573 case POPUPCOMMAND_USETHEIRBLOCK:
2574 m_pwndBottom->UseLeftBlock();
2575 break;
2576 case POPUPCOMMAND_USETHEIRFILE:
2577 m_pwndBottom->UseLeftFile();
2578 break;
2579 // copy, cut and paste commands
2580 case ID_EDIT_COPY:
2581 OnEditCopy();
2582 break;
2583 case ID_EDIT_CUT:
2584 OnEditCut();
2585 break;
2586 case ID_EDIT_PASTE:
2587 OnEditPaste();
2588 break;
2589 // white chars manipulations
2590 case POPUPCOMMAND_TABTOSPACES:
2591 ConvertTabToSpaces();
2592 break;
2593 case POPUPCOMMAND_SPACESTOTABS:
2594 Tabularize();
2595 break;
2596 case POPUPCOMMAND_REMOVETRAILWHITES:
2597 RemoveTrailWhiteChars();
2598 break;
2599 default:
2600 return;
2601 } // switch (cmd)
2602 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2603 return;
2606 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2608 if (!m_pViewData)
2609 return;
2611 int nViewBlockStart = -1;
2612 int nViewBlockEnd = -1;
2613 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2614 if ((point.x >= 0) && (point.y >= 0))
2616 int nLine = GetLineFromPoint(point)-1;
2617 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2619 int nViewLine = GetViewLineForScreen(nLine);
2620 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2622 ClearSelection(); // Clear text-copy selection
2624 nViewBlockStart = nViewLine;
2625 nViewBlockEnd = nViewLine;
2626 DiffStates state = m_pViewData->GetState(nViewLine);
2627 while (nViewBlockStart > 0)
2629 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2630 if (!LinesInOneChange(-1, state, lineState))
2631 break;
2632 nViewBlockStart--;
2635 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2637 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2638 if (!LinesInOneChange(1, state, lineState))
2639 break;
2640 nViewBlockEnd++;
2643 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2644 UpdateCaretPosition(SetupPoint(0, nViewLine));
2649 // FixSelection(); fix selection range
2650 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2651 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2653 DiffStates state = DIFFSTATE_UNKNOWN;
2654 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2656 // find a more 'relevant' state in the selection
2657 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2659 state = m_pViewData->GetState(i);
2660 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2661 break;
2664 OnContextMenu(point, state);
2667 void CBaseView::RefreshViews()
2669 if (m_pwndLeft)
2671 m_pwndLeft->UpdateStatusBar();
2672 m_pwndLeft->Invalidate();
2674 if (m_pwndRight)
2676 m_pwndRight->UpdateStatusBar();
2677 m_pwndRight->Invalidate();
2679 if (m_pwndBottom)
2681 m_pwndBottom->UpdateStatusBar();
2682 m_pwndBottom->Invalidate();
2684 if (m_pwndLocator)
2685 m_pwndLocator->Invalidate();
2688 void CBaseView::GoToFirstDifference()
2690 SetCaretToFirstViewLine();
2691 SelectNextBlock(1, false, false);
2694 void CBaseView::GoToFirstConflict()
2696 SetCaretToFirstViewLine();
2697 SelectNextBlock(1, true, false);
2700 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2702 ClearSelection();
2703 SetupAllSelection(nStart, max(nStart, nEnd));
2705 UpdateCaretPosition(SetupPoint(0, nStart));
2706 Invalidate();
2709 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2711 ClearSelection();
2712 SetupAllViewSelection(nStart, max(nStart, nEnd));
2714 UpdateCaretViewPosition(SetupPoint(0, nStart));
2715 Invalidate();
2718 void CBaseView::SetupAllViewSelection(int start, int end)
2720 SetupViewSelection(m_pwndBottom, start, end);
2721 SetupViewSelection(m_pwndLeft, start, end);
2722 SetupViewSelection(m_pwndRight, start, end);
2725 void CBaseView::SetupAllSelection(int start, int end)
2727 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2730 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2732 void CBaseView::SetupSelection(int start, int end)
2734 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2737 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2739 if (!IsViewGood(view))
2740 return;
2741 view->SetupViewSelection(start, end);
2744 void CBaseView::SetupViewSelection(int start, int end)
2746 // clear text selection before setting line selection ?
2747 m_nSelViewBlockStart = start;
2748 m_nSelViewBlockEnd = end;
2749 Invalidate();
2753 void CBaseView::OnMergePreviousconflict()
2755 SelectNextBlock(-1, true);
2758 void CBaseView::OnMergeNextconflict()
2760 SelectNextBlock(1, true);
2763 void CBaseView::OnMergeNextdifference()
2765 SelectNextBlock(1, false);
2768 void CBaseView::OnMergePreviousdifference()
2770 SelectNextBlock(-1, false);
2773 bool CBaseView::HasNextConflict()
2775 return SelectNextBlock(1, true, true, true);
2778 bool CBaseView::HasPrevConflict()
2780 return SelectNextBlock(-1, true, true, true);
2783 bool CBaseView::HasNextDiff()
2785 return SelectNextBlock(1, false, true, true);
2788 bool CBaseView::HasPrevDiff()
2790 return SelectNextBlock(-1, false, true, true);
2793 bool CBaseView::LinesInOneChange(int direction,
2794 DiffStates initialLineState, DiffStates currentLineState)
2796 // Checks whether all the adjacent lines starting from the initial line
2797 // and up to the current line form the single change
2799 // First of all, if the two lines have identical states, they surely
2800 // belong to one change.
2801 if (initialLineState == currentLineState)
2802 return true;
2804 // Either we move down and initial line state is "added" or "removed" and
2805 // current line state is "empty"...
2806 if (direction > 0)
2808 if (currentLineState == DIFFSTATE_EMPTY)
2810 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2811 return true;
2813 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2814 return true;
2816 // ...or we move up and initial line state is "empty" and current line
2817 // state is "added" or "removed".
2818 if (direction < 0)
2820 if (initialLineState == DIFFSTATE_EMPTY)
2822 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2823 return true;
2825 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2826 return true;
2828 return false;
2831 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2833 if (! m_pViewData)
2834 return false;
2836 const int linesCount = (int)m_Screen2View.size();
2837 if(linesCount == 0)
2838 return false;
2840 int nCenterPos = GetCaretPosition().y;
2841 int nLimit = -1;
2842 if (nDirection > 0)
2843 nLimit = linesCount;
2845 if (nCenterPos >= linesCount)
2846 nCenterPos = linesCount-1;
2848 if (bSkipEndOfCurrentBlock)
2850 // Find end of current block
2851 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2852 while (nCenterPos != nLimit)
2854 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2855 if (!LinesInOneChange(nDirection, state, lineState))
2856 break;
2857 nCenterPos += nDirection;
2861 // Find next diff/conflict block
2862 while (nCenterPos != nLimit)
2864 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2865 if (!bConflict &&
2866 (linestate != DIFFSTATE_NORMAL) &&
2867 (linestate != DIFFSTATE_UNKNOWN) &&
2868 (linestate != DIFFSTATE_FILTEREDDIFF))
2870 break;
2872 if (bConflict &&
2873 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2874 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2875 (linestate == DIFFSTATE_CONFLICTED) ||
2876 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2878 break;
2881 nCenterPos += nDirection;
2883 if (nCenterPos == nLimit)
2884 return false;
2885 if (dryrun)
2886 return (nCenterPos != nLimit);
2888 // Find end of new block
2889 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2890 int nBlockEnd = nCenterPos;
2891 const int maxAllowedLine = nLimit-nDirection;
2892 while (nBlockEnd != maxAllowedLine)
2894 const int lineIndex = nBlockEnd + nDirection;
2895 if (lineIndex >= linesCount)
2896 break;
2897 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2898 if (!LinesInOneChange(nDirection, state, lineState))
2899 break;
2900 nBlockEnd += nDirection;
2903 int nTopPos = nCenterPos - (GetScreenLines()/2);
2904 if (nTopPos < 0)
2905 nTopPos = 0;
2907 POINT ptCaretPos = {0, nCenterPos};
2908 SetCaretPosition(ptCaretPos);
2909 ClearSelection();
2910 if (nDirection > 0)
2911 SetupAllSelection(nCenterPos, nBlockEnd);
2912 else
2913 SetupAllSelection(nBlockEnd, nCenterPos);
2915 ScrollAllToLine(nTopPos, FALSE);
2916 RecalcAllVertScrollBars(TRUE);
2917 SetCaretToLineStart();
2918 EnsureCaretVisible();
2919 OnNavigateNextinlinediff();
2921 UpdateViewsCaretPosition();
2922 UpdateCaret();
2923 ShowDiffLines(nCenterPos);
2924 return true;
2927 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2929 if (pNMHDR->idFrom != (UINT_PTR)m_hWnd)
2930 return FALSE;
2932 CString strTipText;
2933 strTipText = m_sWindowName + L"\r\n" + m_sFullFilePath;
2935 DWORD pos = GetMessagePos();
2936 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2937 ScreenToClient(&point);
2938 const int nLine = GetButtonEventLineIndex(point);
2940 if (nLine >= 0)
2942 int nViewLine = GetViewLineForScreen(nLine);
2943 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2945 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2946 if (movedIndex >= 0)
2948 if (m_pViewData->IsMovedFrom(nViewLine))
2950 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2952 else
2954 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2961 *pResult = 0;
2962 if (strTipText.IsEmpty())
2963 return TRUE;
2965 // need to handle both ANSI and UNICODE versions of the message
2966 if (pNMHDR->code == TTN_NEEDTEXTA)
2968 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2969 pTTTA->lpszText = m_szTip;
2970 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2972 else
2974 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2975 lstrcpyn(m_wszTip, strTipText, min(strTipText.GetLength() + 1, _countof(m_wszTip) - 1));
2976 pTTTW->lpszText = m_wszTip;
2979 return TRUE; // message was handled
2982 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2984 CRect rcClient;
2985 GetClientRect(rcClient);
2986 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2988 int marginwidth = GetSystemMetrics(SM_CXSMICON) + 2 + 2;
2989 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2991 marginwidth += (m_nDigits * m_nCharWidth) + 2;
2993 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2995 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2997 // inside the header part of the view (showing the filename)
2998 pTI->hwnd = this->m_hWnd;
2999 this->GetClientRect(&pTI->rect);
3000 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
3001 pTI->uId = (UINT_PTR)m_hWnd;
3002 pTI->lpszText = LPSTR_TEXTCALLBACK;
3004 // we want multi line tooltips
3005 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
3006 if (pToolTip->GetSafeHwnd())
3007 pToolTip->SetMaxTipWidth(SHRT_MAX);
3009 return (textrect.PtInRect(point) ? 1 : 2);
3012 return -1;
3015 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
3017 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
3018 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3020 switch (nChar)
3022 case VK_TAB:
3023 if (bControl)
3025 if (this==m_pwndLeft)
3027 if (IsViewGood(m_pwndRight))
3029 m_pwndRight->SetFocus();
3031 else if (IsViewGood(m_pwndBottom))
3033 m_pwndBottom->SetFocus();
3036 else if (this==m_pwndRight)
3038 if (IsViewGood(m_pwndBottom))
3040 m_pwndBottom->SetFocus();
3042 else if (IsViewGood(m_pwndLeft))
3044 m_pwndLeft->SetFocus();
3047 else if (this==m_pwndBottom)
3049 if (IsViewGood(m_pwndLeft))
3051 m_pwndLeft->SetFocus();
3053 else if (IsViewGood(m_pwndRight))
3055 m_pwndRight->SetFocus();
3059 break;
3060 case VK_PRIOR:
3062 POINT ptCaretPos = GetCaretPosition();
3063 ptCaretPos.y -= GetScreenLines();
3064 ptCaretPos.y = max(ptCaretPos.y, 0);
3065 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3066 SetCaretPosition(ptCaretPos);
3067 OnCaretMove(MOVELEFT, bShift);
3068 ShowDiffLines(ptCaretPos.y);
3070 break;
3071 case VK_NEXT:
3073 POINT ptCaretPos = GetCaretPosition();
3074 ptCaretPos.y += GetScreenLines();
3075 if (ptCaretPos.y >= GetLineCount())
3076 ptCaretPos.y = GetLineCount()-1;
3077 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3078 SetCaretPosition(ptCaretPos);
3079 OnCaretMove(MOVERIGHT, bShift);
3080 ShowDiffLines(ptCaretPos.y);
3082 break;
3083 case VK_HOME:
3085 if (bControl)
3087 ScrollAllToLine(0);
3088 SetCaretToViewStart();
3089 m_nCaretGoalPos = 0;
3090 if (bShift)
3091 AdjustSelection(MOVELEFT);
3092 else
3093 ClearSelection();
3094 UpdateCaret();
3096 else
3098 POINT ptCaretPos = GetCaretPosition();
3099 CString sLine = GetLineChars(ptCaretPos.y);
3100 int pos = 0;
3101 while (pos < sLine.GetLength())
3103 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3104 break;
3105 ++pos;
3107 if (ptCaretPos.x == pos)
3109 SetCaretToLineStart();
3110 m_nCaretGoalPos = 0;
3111 OnCaretMove(MOVERIGHT, bShift);
3112 ScrollAllToChar(0);
3114 else
3116 ptCaretPos.x = pos;
3117 SetCaretAndGoalPosition(ptCaretPos);
3118 OnCaretMove(MOVELEFT, bShift);
3122 break;
3123 case VK_END:
3125 if (bControl)
3127 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3128 POINT ptCaretPos;
3129 ptCaretPos.y = GetLineCount()-1;
3130 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3131 SetCaretAndGoalPosition(ptCaretPos);
3132 if (bShift)
3133 AdjustSelection(MOVERIGHT);
3134 else
3135 ClearSelection();
3137 else
3139 POINT ptCaretPos = GetCaretPosition();
3140 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3141 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3143 ptCaretPos.x--;
3145 SetCaretAndGoalPosition(ptCaretPos);
3146 OnCaretMove(MOVERIGHT, bShift);
3149 break;
3150 case VK_BACK:
3151 if (IsWritable())
3153 if (! HasTextSelection())
3155 POINT ptCaretPos = GetCaretPosition();
3156 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3157 break;
3158 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3159 if (bControl)
3160 MoveCaretWordLeft();
3161 else
3163 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3167 m_ptSelectionViewPosStart = GetCaretViewPosition();
3169 RemoveSelectedText();
3171 break;
3172 case VK_DELETE:
3173 if (IsWritable())
3175 if (! HasTextSelection())
3177 if (bControl)
3179 m_ptSelectionViewPosStart = GetCaretViewPosition();
3180 MoveCaretWordRight();
3181 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3183 else
3185 if (! MoveCaretRight())
3186 break;
3187 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3188 MoveCaretLeft();
3189 m_ptSelectionViewPosStart = GetCaretViewPosition();
3192 RemoveSelectedText();
3194 break;
3195 case VK_INSERT:
3196 m_bInsertMode = !m_bInsertMode;
3197 UpdateCaret();
3198 break;
3200 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3203 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3205 const int nClickedLine = GetButtonEventLineIndex(point);
3206 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3208 POINT ptCaretPos;
3209 ptCaretPos.y = nClickedLine;
3210 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3211 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3212 SetCaretAndGoalPosition(ptCaretPos);
3214 if (nFlags & MK_SHIFT)
3215 AdjustSelection(MOVERIGHT);
3216 else
3218 ClearSelection();
3219 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3220 if (point.x < GetMarginWidth())
3222 // select the whole line
3223 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3224 m_ptSelectionViewPosStart.x = 0;
3225 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3229 UpdateViewsCaretPosition();
3230 Invalidate();
3233 CView::OnLButtonDown(nFlags, point);
3236 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3238 if (zChar == ' ' || zChar == '\t' )
3240 return CHG_WHITESPACE;
3242 if (zChar < 0x20)
3244 return CHG_CONTROL;
3246 if (m_sWordSeparators.Find(zChar) >= 0)
3248 return CHG_WORDSEPARATOR;
3250 return CHG_WORDLETTER;
3253 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3255 if (m_pViewData == 0) {
3256 CView::OnLButtonDblClk(nFlags, point);
3257 return;
3260 const int nClickedLine = GetButtonEventLineIndex(point);
3261 if ( nClickedLine < 0)
3262 return;
3263 int nViewLine = GetViewLineForScreen(nClickedLine);
3264 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3266 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3268 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3270 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3271 int screenLine = FindViewLineNumber(movedindex);
3272 int nTop = screenLine - GetScreenLines()/2;
3273 if (nTop < 0)
3274 nTop = 0;
3275 ScrollAllToLine(nTop);
3276 // find and select the whole moved block
3277 int startSel = movedindex;
3278 int endSel = movedindex;
3279 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3280 startSel--;
3281 startSel++;
3282 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3283 endSel++;
3284 endSel--;
3285 m_pOtherView->SetupSelection(startSel, endSel);
3286 return CView::OnLButtonDblClk(nFlags, point);
3290 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3292 // a double click on a marker expands the hidden text
3293 int i = nViewLine;
3294 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3296 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3297 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3298 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3299 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3300 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3301 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3302 i++;
3304 BuildAllScreen2ViewVector();
3305 if (m_pwndLeft)
3306 m_pwndLeft->Invalidate();
3307 if (m_pwndRight)
3308 m_pwndRight->Invalidate();
3309 if (m_pwndBottom)
3310 m_pwndBottom->Invalidate();
3312 else
3314 POINT ptCaretPos;
3315 ptCaretPos.y = nClickedLine;
3316 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3317 SetCaretPosition(ptCaretPos);
3318 ClearSelection();
3320 POINT ptViewCarret = GetCaretViewPosition();
3321 nViewLine = ptViewCarret.y;
3322 if (nViewLine >= GetViewCount())
3323 return;
3324 const CString &sLine = GetViewLine(nViewLine);
3325 int nLineLength = sLine.GetLength();
3326 int nBasePos = ptViewCarret.x;
3327 // get target char group
3328 ECharGroup eLeft = CHG_UNKNOWN;
3329 if (nBasePos > 0)
3331 eLeft = GetCharGroup(sLine[nBasePos-1]);
3333 ECharGroup eRight = CHG_UNKNOWN;
3334 if (nBasePos < nLineLength)
3336 eRight = GetCharGroup(sLine[nBasePos]);
3338 ECharGroup eTarget = max(eRight, eLeft);
3339 // find left margin
3340 int nLeft = nBasePos;
3341 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3343 nLeft--;
3345 // get right margin
3346 int nRight = nBasePos;
3347 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3349 nRight++;
3351 // set selection
3352 m_ptSelectionViewPosStart.x = nLeft;
3353 m_ptSelectionViewPosStart.y = nViewLine;
3354 m_ptSelectionViewPosEnd.x = nRight;
3355 m_ptSelectionViewPosEnd.y = nViewLine;
3356 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3357 SetupAllViewSelection(nViewLine, nViewLine);
3358 // set caret
3359 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3360 UpdateViewsCaretPosition();
3361 UpdateGoalPos();
3363 // set mark word
3364 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3365 int nMarkWidth = max(nRight - nLeft, 0);
3366 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3367 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3369 m_sMarkedWord.Empty();
3372 if (m_pwndLeft)
3373 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3374 if (m_pwndRight)
3375 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3376 if (m_pwndBottom)
3377 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3379 Invalidate();
3380 if (m_pwndLocator)
3381 m_pwndLocator->Invalidate();
3384 CView::OnLButtonDblClk(nFlags, point);
3387 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3389 const int nClickedLine = GetButtonEventLineIndex(point);
3390 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3392 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3394 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3395 if (pidl)
3397 SHOpenFolderAndSelectItems(pidl,0,0,0);
3398 CoTaskMemFree((LPVOID)pidl);
3401 return;
3403 POINT ptCaretPos;
3404 ptCaretPos.y = nClickedLine;
3405 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3406 SetCaretAndGoalPosition(ptCaretPos);
3407 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3408 if (m_pwndLeft)
3409 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3410 if (m_pwndRight)
3411 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3412 if (m_pwndBottom)
3413 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3414 ClearSelection();
3415 m_ptSelectionViewPosStart.x = 0;
3416 m_ptSelectionViewPosStart.y = nClickedLine;
3417 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3418 m_ptSelectionViewPosEnd.y = nClickedLine;
3419 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3420 UpdateViewsCaretPosition();
3421 Invalidate();
3422 if (m_pwndLocator)
3423 m_pwndLocator->Invalidate();
3426 void CBaseView::OnEditCopy()
3428 CString sCopyData = GetSelectedText();
3430 if (!sCopyData.IsEmpty())
3432 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3436 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3438 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3440 --m_pMainFrame->m_nMoveMovesToIgnore;
3441 CView::OnMouseMove(nFlags, point);
3442 return;
3444 int nMouseLine = GetButtonEventLineIndex(point);
3445 if (nMouseLine < -1)
3446 nMouseLine = -1;
3447 m_mouseInMargin = point.x < GetMarginWidth();
3449 ShowDiffLines(nMouseLine);
3451 KillTimer(IDT_SCROLLTIMER);
3452 if (nFlags & MK_LBUTTON)
3454 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3455 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3456 if (saveMouseLine < 0)
3457 return;
3458 int col = CalcColFromPoint(point.x, saveMouseLine);
3459 int charIndex = CalculateCharIndex(saveMouseLine, col);
3460 if (HasSelection() &&
3461 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3463 POINT ptCaretPos = {charIndex, nMouseLine};
3464 SetCaretAndGoalPosition(ptCaretPos);
3465 AdjustSelection(MOVERIGHT);
3466 Invalidate();
3467 UpdateWindow();
3469 if (nMouseLine < m_nTopLine)
3471 ScrollAllToLine(m_nTopLine-1, TRUE);
3472 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3474 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3476 ScrollAllToLine(m_nTopLine+1, TRUE);
3477 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3479 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3481 ScrollAllSide(-1);
3482 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3484 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3486 ScrollAllSide(1);
3487 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3489 SetCapture();
3493 CView::OnMouseMove(nFlags, point);
3496 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3498 ShowDiffLines(-1);
3499 ReleaseCapture();
3500 KillTimer(IDT_SCROLLTIMER);
3502 __super::OnLButtonUp(nFlags, point);
3505 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3507 if (nIDEvent == IDT_SCROLLTIMER)
3509 POINT point;
3510 GetCursorPos(&point);
3511 ScreenToClient(&point);
3512 int nMouseLine = GetButtonEventLineIndex(point);
3513 if (nMouseLine < -1)
3515 nMouseLine = -1;
3517 if (GetKeyState(VK_LBUTTON)&0x8000)
3519 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3520 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3521 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3522 if (nMouseLine < m_nTopLine)
3524 ScrollAllToLine(m_nTopLine-1, TRUE);
3525 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3527 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3529 ScrollAllToLine(m_nTopLine+1, TRUE);
3530 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3532 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3534 ScrollAllSide(-1);
3535 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3537 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3539 ScrollAllSide(1);
3540 SetTimer(IDT_SCROLLTIMER, 20, nullptr);
3546 CView::OnTimer(nIDEvent);
3549 void CBaseView::ShowDiffLines(int nLine)
3551 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3553 m_pwndLineDiffBar->ShowLines(nLine);
3554 nLine = -1;
3555 m_nMouseLine = nLine;
3556 return;
3559 if ((!m_pwndRight)||(!m_pwndLeft))
3560 return;
3561 if(m_pMainFrame->m_bOneWay)
3562 return;
3564 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3565 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3567 if (nLine < 0)
3568 return;
3570 if (nLine != m_nMouseLine)
3572 if (nLine >= GetLineCount())
3573 nLine = -1;
3574 m_nMouseLine = nLine;
3575 m_pwndLineDiffBar->ShowLines(nLine);
3577 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3580 const viewdata& CBaseView::GetEmptyLineData()
3582 static const viewdata emptyLine(L"", DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3583 return emptyLine;
3586 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3588 for (int i = 0; i < nCount; i++)
3590 InsertViewData(nFirstView, GetEmptyLineData());
3595 void CBaseView::UpdateCaret()
3597 POINT ptCaretPos = GetCaretPosition();
3598 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3599 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3600 SetCaretPosition(ptCaretPos);
3602 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3604 if (m_bFocused &&
3605 ptCaretPos.y >= m_nTopLine &&
3606 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3607 nCaretOffset >= m_nOffsetChar &&
3608 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3610 POINT pt1 = TextToClient(ptCaretPos);
3611 if (m_bInsertMode)
3612 CreateSolidCaret(2, GetLineHeight());
3613 else
3615 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3616 POINT pt2 = TextToClient(pt);
3617 int width = max(GetCharWidth(), pt2.x - pt1.x);
3618 CreateSolidCaret(width, GetLineHeight());
3620 SetCaretPos(pt1);
3621 ShowCaret();
3623 else
3625 HideCaret();
3629 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3631 POINT ptViewPos;
3632 ptViewPos.x = pt.x;
3634 int nSubLine = GetSubLineOffset(pt.y);
3635 if (nSubLine > 0)
3637 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3639 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3643 ptViewPos.y = GetViewLineForScreen(pt.y);
3644 return ptViewPos;
3647 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3649 POINT ptPos;
3650 int nViewLineLenLeft = GetViewLineLength(pt.y);
3651 ptPos.x = min(nViewLineLenLeft, pt.x);
3652 ptPos.y = FindScreenLineForViewLine(pt.y);
3653 if (GetViewLineForScreen(ptPos.y) != pt.y )
3655 ptPos.x = 0;
3657 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3659 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3660 while (nSubLineLength < ptPos.x)
3662 ptPos.x -= nSubLineLength;
3663 nViewLineLenLeft -= nSubLineLength;
3664 ptPos.y++;
3665 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3667 // last pos of non last sub-line go to start of next screen line
3668 // Note: while this works correctly, it's not what a user might expect:
3669 // cursor-right when the caret is before the last char of a wrapped line
3670 // now moves the caret to the next line. But users expect the caret to
3671 // move to the right of the last char instead, and with another cursor-right
3672 // keystroke to move the caret to the next line.
3673 // Basically, this would require to handle two caret positions for the same
3674 // logical position in the line string (one on the last position of the first line,
3675 // one on the first position of the new line. For non-wrapped lines this works
3676 // because there's an 'invisible' newline char at the end of the first line.
3677 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3679 ptPos.x = 0;
3680 ptPos.y++;
3684 return ptPos;
3688 void CBaseView::EnsureCaretVisible()
3690 POINT ptCaretPos = GetCaretPosition();
3691 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3693 if (ptCaretPos.y < m_nTopLine)
3694 ScrollAllToLine(ptCaretPos.y);
3695 int screnLines = GetScreenLines();
3696 if (screnLines)
3698 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3699 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3700 if (nCaretOffset < m_nOffsetChar)
3701 ScrollAllToChar(nCaretOffset);
3702 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3703 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3707 int CBaseView::CalculateActualOffset(const POINT& point)
3709 int nLineIndex = point.y;
3710 int nCharIndex = point.x;
3711 ASSERT(nCharIndex >= 0);
3712 CString sLine = GetLineChars(nLineIndex);
3713 int nLineLength = sLine.GetLength();
3714 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3717 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3719 int nLength = GetLineLength(nLineIndex);
3720 int nSubLine = GetSubLineOffset(nLineIndex);
3721 if (nSubLine>=0)
3723 int nViewLine = GetViewLineForScreen(nLineIndex);
3724 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3726 int nMultilineCount = CountMultiLines(nViewLine);
3727 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3729 nLength--;
3733 CString Line = GetLineChars(nLineIndex);
3734 int nIndex = 0;
3735 int nOffset = 0;
3736 int nTabSize = GetTabSize();
3737 while (nOffset < nActualOffset && nIndex < nLength)
3739 if (Line.GetAt(nIndex) == L'\t')
3740 nOffset += (nTabSize - nOffset % nTabSize);
3741 else
3742 ++nOffset;
3743 ++nIndex;
3745 return nIndex;
3749 * @param xpos X coordinate in CBaseView
3750 * @param lineIndex logical line index (e.g. wrap/collapse)
3752 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3754 int xpos2;
3755 CDC *pDC = GetDC();
3756 if (pDC)
3758 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3759 int fit = text.GetLength();
3760 auto posBuffer = std::make_unique<int[]>(fit);
3761 pDC->SelectObject(GetFont()); // is this right font ?
3762 SIZE size;
3763 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3764 ReleaseDC(pDC);
3765 int lower = -1, upper = fit - 1;
3766 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3769 int middle = (upper + lower + 1) / 2;
3770 int width = posBuffer[middle];
3771 if (xcheck < width)
3772 upper = middle - 1;
3773 else
3774 lower = middle;
3775 } while (lower < upper);
3776 lower++;
3777 xpos2 = lower;
3778 if (lower < fit - 1)
3780 int charWidth = posBuffer[lower] - (lower > 0 ? posBuffer[lower - 1] : 0);
3781 if (posBuffer[lower] - xcheck <= charWidth / 2)
3782 xpos2++;
3785 else
3787 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3788 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3789 xpos2++;
3791 return xpos2;
3794 POINT CBaseView::TextToClient(const POINT& point)
3796 POINT pt;
3797 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3798 pt.y = nOffsetScreenLine * GetLineHeight();
3799 pt.x = CalculateActualOffset(point);
3801 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3802 CDC * pDC = GetDC();
3803 if (pDC)
3805 pDC->SelectObject(GetFont()); // is this right font ?
3806 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3807 CString sLine = GetLineChars(nScreenLine);
3808 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3809 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3810 ReleaseDC(pDC);
3811 } else {
3812 nLeft += pt.x * GetCharWidth();
3815 pt.x = nLeft;
3816 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3817 return pt;
3820 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3822 CView::OnChar(nChar, nRepCnt, nFlags);
3824 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3825 bool bSkipSelectionClear = false;
3827 if (IsReadonly())
3828 return;
3830 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3831 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3833 return;
3836 if (!m_pViewData) // no data - nothing to do
3837 return;
3839 if (nChar == VK_F16)
3841 // generated by a ctrl+backspace - ignore.
3843 else if (nChar==VK_TAB && HasTextLineSelection())
3845 // change indentation for selected lines
3846 if (bShift)
3848 RemoveIndentationForSelectedBlock();
3850 else
3852 AddIndentationForSelectedBlock();
3854 bSkipSelectionClear = true;
3856 else if ((nChar > 31)||(nChar == VK_TAB))
3858 ResetUndoStep();
3859 RemoveSelectedText();
3860 POINT ptCaretViewPos = GetCaretViewPosition();
3861 int nViewLine = ptCaretViewPos.y;
3862 if ((nViewLine==0)&&(GetViewCount()==0))
3863 OnChar(VK_RETURN, 0, 0);
3864 int charCount = 1;
3865 viewdata lineData = GetViewData(nViewLine);
3866 if (nChar == VK_TAB)
3868 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3869 if (indentChars > 0)
3871 lineData.sLine.Insert(ptCaretViewPos.x, CString(L' ', indentChars));
3872 charCount = indentChars;
3874 else
3875 lineData.sLine.Insert(ptCaretViewPos.x, L'\t');
3877 else
3879 if (m_bInsertMode)
3880 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3881 else
3883 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3884 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3885 else
3886 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3889 if (IsStateEmpty(lineData.state))
3891 // if not last line set EOL
3892 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3894 if (!IsViewLineEmpty(nCheckViewLine))
3896 lineData.ending = m_lineendings;
3897 break;
3900 // make sure previous (non empty) line have EOL set
3901 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3903 if (!IsViewLineEmpty(nCheckViewLine))
3905 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3907 SetViewLineEnding(nCheckViewLine, m_lineendings);
3909 break;
3913 lineData.state = DIFFSTATE_EDITED;
3914 bool bNeedRenumber = false;
3915 if (lineData.linenumber == -1)
3917 lineData.linenumber = 0;
3918 bNeedRenumber = true;
3920 SetViewData(nViewLine, lineData);
3921 SetModified();
3922 SaveUndoStep();
3923 BuildAllScreen2ViewVector(nViewLine);
3924 if (bNeedRenumber)
3926 UpdateViewLineNumbers();
3928 for (int i = 0; i < charCount; ++i)
3929 MoveCaretRight();
3930 UpdateGoalPos();
3932 else if (nChar == 10)
3934 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3935 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3936 EOL newEOL = EOL_CRLF;
3937 switch (eol)
3939 case EOL_CRLF:
3940 newEOL = EOL_CR;
3941 break;
3942 case EOL_CR:
3943 newEOL = EOL_LF;
3944 break;
3945 case EOL_LF:
3946 newEOL = EOL_CRLF;
3947 break;
3949 if (eol==EOL_NOENDING || eol==newEOL)
3950 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3951 // to add EOL on newly edited empty line hit enter
3952 // don't store into UNDO if no change happened
3953 // and don't mark file as modified
3954 return;
3955 AddUndoViewLine(nViewLine);
3956 m_pViewData->SetLineEnding(nViewLine, newEOL);
3957 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3958 UpdateGoalPos();
3960 else if (nChar == VK_RETURN)
3962 // insert a new, fresh and empty line below the cursor
3963 RemoveSelectedText();
3965 CUndo::GetInstance().BeginGrouping();
3967 POINT ptCaretViewPos = GetCaretViewPosition();
3968 int nViewLine = ptCaretViewPos.y;
3969 int nLeft = ptCaretViewPos.x;
3970 CString sLine = GetViewLineChars(nViewLine);
3971 CString sLineLeft = sLine.Left(nLeft);
3972 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3973 EOL eOriginalEnding = EOL_AUTOLINE;
3974 if (m_pViewData->GetCount() > nViewLine)
3975 eOriginalEnding = GetViewLineEnding(nViewLine);
3977 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3979 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3980 SetViewData(nViewLine, newFirstLine);
3983 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3984 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3985 InsertViewData(nInsertLine, newLastLine);
3986 SetModified();
3987 SaveUndoStep();
3989 // adds new line everywhere except me
3990 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3992 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3994 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3996 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3998 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
4000 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
4002 SaveUndoStep();
4004 UpdateViewLineNumbers();
4005 SaveUndoStep();
4006 CUndo::GetInstance().EndGrouping();
4008 BuildAllScreen2ViewVector();
4009 // move the cursor to the new line
4010 ptCaretViewPos = SetupPoint(0, nViewLine+1);
4011 SetCaretAndGoalViewPosition(ptCaretViewPos);
4013 else
4014 return; // Unknown control character -- ignore it.
4015 if (!bSkipSelectionClear)
4016 ClearSelection();
4017 EnsureCaretVisible();
4018 UpdateCaret();
4019 Invalidate(FALSE);
4022 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
4024 ResetUndoStep();
4025 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
4026 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
4027 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
4028 SetModified();
4029 SaveUndoStep();
4030 RecalcAllVertScrollBars();
4031 Invalidate(FALSE);
4034 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
4036 if (!m_pViewData)
4037 return;
4038 int viewLine = nViewLineIndex;
4039 EOL ending = m_pViewData->GetLineEnding(viewLine);
4040 if (ending == EOL_NOENDING)
4042 ending = m_lineendings;
4044 viewdata newLine(L"", DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
4045 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
4047 CString sPartLine = GetViewLineChars(nViewLineIndex);
4048 int nPosx = GetCaretPosition().x; // should be view pos ?
4049 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
4050 sPartLine = sPartLine.Mid(nPosx);
4051 newLine.sLine = sPartLine;
4053 m_pViewData->InsertData(viewLine+1, newLine);
4054 BuildAllScreen2ViewVector();
4057 void CBaseView::RemoveSelectedText()
4059 if (!m_pViewData)
4060 return;
4061 if (!HasTextSelection())
4062 return;
4064 // fix selection if starts or ends on empty line
4065 SetCaretViewPosition(m_ptSelectionViewPosEnd);
4066 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4069 m_ptSelectionViewPosEnd = GetCaretViewPosition();
4070 SetCaretViewPosition(m_ptSelectionViewPosStart);
4071 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4074 m_ptSelectionViewPosStart = GetCaretViewPosition();
4075 if (!HasTextSelection())
4077 ClearSelection();
4078 return;
4081 // We want to undo the insertion in a single step.
4082 ResetUndoStep();
4083 CUndo::GetInstance().BeginGrouping();
4085 // combine first and last line
4086 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
4087 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
4088 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
4089 oFirstLine.ending = oLastLine.ending;
4090 oFirstLine.state = DIFFSTATE_EDITED;
4091 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4093 // clean up middle lines if any
4094 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4096 viewdata oEmptyLine = GetEmptyLineData();
4097 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4099 SetViewData(nViewLine, oEmptyLine);
4101 SaveUndoStep();
4103 if (CleanEmptyLines())
4105 BuildAllScreen2ViewVector(); // schedule full rebuild
4107 SaveUndoStep();
4108 UpdateViewLineNumbers();
4111 SetModified(); //TODO set modified only if real data was changed
4112 SaveUndoStep();
4113 CUndo::GetInstance().EndGrouping();
4115 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4116 SetCaretViewPosition(m_ptSelectionViewPosStart);
4117 UpdateGoalPos();
4118 ClearSelection();
4119 UpdateCaret();
4120 EnsureCaretVisible();
4121 Invalidate(FALSE);
4124 void CBaseView::PasteText()
4126 if (!OpenClipboard())
4127 return;
4129 CString sClipboardText;
4130 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4131 if (hglb)
4133 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4134 sClipboardText = CString(lpstr);
4135 GlobalUnlock(hglb);
4137 hglb = GetClipboardData(CF_UNICODETEXT);
4138 if (hglb)
4140 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4141 sClipboardText = lpstr;
4142 GlobalUnlock(hglb);
4144 CloseClipboard();
4146 if (sClipboardText.IsEmpty())
4147 return;
4149 sClipboardText.Replace(L"\r\n", L"\r");
4150 sClipboardText.Replace('\n', '\r');
4152 InsertText(sClipboardText);
4155 void CBaseView::OnCaretDown()
4157 POINT ptCaretPos = GetCaretPosition();
4158 int nLine = ptCaretPos.y;
4159 int nNextLine = nLine + 1;
4160 if (nNextLine >= GetLineCount()) // already at last line
4162 return;
4165 POINT ptCaretViewPos = GetCaretViewPosition();
4166 int nViewLine = ptCaretViewPos.y;
4167 int nNextViewLine = GetViewLineForScreen(nNextLine);
4168 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4170 // find next suitable screen line
4171 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4173 nNextLine++;
4174 if (nNextLine >= GetLineCount())
4176 return;
4178 nNextViewLine = GetViewLineForScreen(nNextLine);
4181 ptCaretPos.y = nNextLine;
4182 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4183 SetCaretPosition(ptCaretPos);
4184 OnCaretMove(MOVELEFT);
4185 ShowDiffLines(ptCaretPos.y);
4188 bool CBaseView::MoveCaretLeft()
4190 POINT ptCaretViewPos = GetCaretViewPosition();
4192 //int nViewLine = ptCaretViewPos.y;
4193 if (ptCaretViewPos.x == 0)
4195 int nPrevLine = GetCaretPosition().y;
4196 int nPrevViewLine;
4197 do {
4198 nPrevLine--;
4199 if (nPrevLine < 0)
4201 return false;
4203 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4204 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4205 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4206 ShowDiffLines(nPrevLine);
4208 else
4209 --ptCaretViewPos.x;
4211 SetCaretAndGoalViewPosition(ptCaretViewPos);
4212 return true;
4215 bool CBaseView::MoveCaretRight()
4217 POINT ptCaretViewPos = GetCaretViewPosition();
4219 int nViewLine = ptCaretViewPos.y;
4220 int nViewLineLen = GetViewLineLength(nViewLine);
4221 if (ptCaretViewPos.x >= nViewLineLen)
4223 int nNextLine = GetCaretPosition().y;
4224 int nNextViewLine;
4225 do {
4226 nNextLine++;
4227 if (nNextLine >= GetLineCount())
4229 return false;
4231 nNextViewLine = GetViewLineForScreen(nNextLine);
4232 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4233 ptCaretViewPos.y = nNextViewLine;
4234 ptCaretViewPos.x = 0;
4235 ShowDiffLines(nNextLine);
4237 else
4238 ++ptCaretViewPos.x;
4240 SetCaretAndGoalViewPosition(ptCaretViewPos);
4241 return true;
4244 void CBaseView::UpdateGoalPos()
4246 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4249 void CBaseView::OnCaretLeft()
4251 MoveCaretLeft();
4252 OnCaretMove(MOVELEFT);
4255 void CBaseView::OnCaretRight()
4257 MoveCaretRight();
4258 OnCaretMove(MOVERIGHT);
4261 void CBaseView::OnCaretUp()
4263 POINT ptCaretPos = GetCaretPosition();
4264 int nLine = ptCaretPos.y;
4265 if (nLine <= 0) // already at first line
4267 return;
4269 int nPrevLine = nLine - 1;
4271 POINT ptCaretViewPos = GetCaretViewPosition();
4272 int nViewLine = ptCaretViewPos.y;
4273 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4274 if (nPrevViewLine != nViewLine) // not on same view line
4276 // find previous suitable screen line
4277 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4279 if (nPrevLine <= 0)
4281 return;
4283 nPrevLine--;
4284 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4287 ptCaretPos.y = nPrevLine;
4288 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4289 SetCaretPosition(ptCaretPos);
4290 OnCaretMove(MOVELEFT);
4291 ShowDiffLines(ptCaretPos.y);
4294 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4296 switch (GetCharGroup(ch))
4298 case CHG_CONTROL:
4299 case CHG_WHITESPACE:
4300 case CHG_WORDSEPARATOR:
4301 return true;
4303 return false;
4306 bool CBaseView::IsCaretAtWordBoundary()
4308 POINT ptViewCaret = GetCaretViewPosition();
4309 CString line = GetViewLineChars(ptViewCaret.y);
4310 if (line.IsEmpty())
4311 return false; // no boundary at the empty lines
4312 if (ptViewCaret.x == 0)
4313 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4314 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4315 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4316 return
4317 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4318 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4321 void CBaseView::UpdateViewsCaretPosition()
4323 POINT ptCaretPos = GetCaretPosition();
4324 if (m_pwndBottom && m_pwndBottom!=this)
4325 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4326 if (m_pwndLeft && m_pwndLeft!=this)
4327 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4328 if (m_pwndRight && m_pwndRight!=this)
4329 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4332 void CBaseView::OnCaretWordleft()
4334 MoveCaretWordLeft();
4335 OnCaretMove(MOVELEFT);
4338 void CBaseView::OnCaretWordright()
4340 MoveCaretWordRight();
4341 OnCaretMove(MOVERIGHT);
4344 void CBaseView::MoveCaretWordLeft()
4346 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4351 void CBaseView::MoveCaretWordRight()
4353 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4358 void CBaseView::ClearCurrentSelection()
4360 m_ptSelectionViewPosStart = GetCaretViewPosition();
4361 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4362 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4363 m_nSelViewBlockStart = -1;
4364 m_nSelViewBlockEnd = -1;
4365 Invalidate(FALSE);
4368 void CBaseView::ClearSelection()
4370 if (m_pwndLeft)
4371 m_pwndLeft->ClearCurrentSelection();
4372 if (m_pwndRight)
4373 m_pwndRight->ClearCurrentSelection();
4374 if (m_pwndBottom)
4375 m_pwndBottom->ClearCurrentSelection();
4378 void CBaseView::AdjustSelection(bool bMoveLeft)
4380 POINT ptCaretViewPos = GetCaretViewPosition();
4381 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4383 // select all have been used recently update origin
4384 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4386 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4387 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4389 m_ptSelectionViewPosStart = ptCaretViewPos;
4390 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4392 else
4394 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4395 m_ptSelectionViewPosEnd = ptCaretViewPos;
4398 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4400 Invalidate(FALSE);
4403 void CBaseView::OnEditCut()
4405 if (IsWritable())
4407 OnEditCopy();
4408 RemoveSelectedText();
4412 void CBaseView::OnEditPaste()
4414 if (IsWritable())
4416 CUndo::GetInstance().BeginGrouping();
4417 RemoveSelectedText();
4418 PasteText();
4419 CUndo::GetInstance().EndGrouping();
4423 void CBaseView::DeleteFonts()
4425 for (int i=0; i<fontsCount; i++)
4427 if (m_apFonts[i])
4429 m_apFonts[i]->DeleteObject();
4430 delete m_apFonts[i];
4431 m_apFonts[i] = nullptr;
4436 void CBaseView::OnCaretMove(bool bMoveLeft)
4438 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4439 OnCaretMove(bMoveLeft, bShift);
4442 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4444 if(isShiftPressed)
4445 AdjustSelection(bMoveLeft);
4446 else
4447 ClearSelection();
4448 EnsureCaretVisible();
4449 UpdateCaret();
4452 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4454 AddCutCopyAndPaste(popup);
4457 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4459 popup.AppendMenu(MF_SEPARATOR, NULL);
4460 CString temp;
4461 temp.LoadString(IDS_EDIT_COPY);
4462 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4463 if (IsWritable())
4465 temp.LoadString(IDS_EDIT_CUT);
4466 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4467 temp.LoadString(IDS_EDIT_PASTE);
4468 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4469 popup.AppendMenu(MF_SEPARATOR, NULL);
4473 void CBaseView::CompensateForKeyboard(CPoint& point)
4475 // if the context menu is invoked through the keyboard, we have to use
4476 // a calculated position on where to anchor the menu on
4477 if (ArePointsSame(point, SetupPoint(-1, -1)))
4479 CRect rect;
4480 GetWindowRect(&rect);
4481 point = rect.CenterPoint();
4485 HICON CBaseView::LoadIcon(WORD iconId)
4487 int iconWidth = GetSystemMetrics(SM_CXSMICON);
4488 int iconHeight = GetSystemMetrics(SM_CYSMICON);
4489 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4490 IMAGE_ICON, iconWidth, iconHeight, LR_DEFAULTCOLOR);
4491 return (HICON)icon;
4494 void CBaseView::ReleaseBitmap()
4496 if (m_pCacheBitmap)
4498 m_pCacheBitmap->DeleteObject();
4499 delete m_pCacheBitmap;
4500 m_pCacheBitmap = nullptr;
4504 void CBaseView::BuildMarkedWordArray()
4506 int lineCount = GetLineCount();
4507 m_arMarkedWordLines.clear();
4508 m_arMarkedWordLines.reserve(lineCount);
4509 bool bDoit = !m_sMarkedWord.IsEmpty();
4510 for (int i = 0; i < lineCount; ++i)
4512 if (bDoit)
4514 CString line = GetLineChars(i);
4516 if (!line.IsEmpty())
4518 int found = 0;
4519 int nMarkStart = -1;
4520 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4522 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4523 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4524 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4525 if (eLeft != eStart)
4527 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4528 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4529 if (eRight != eEnd)
4531 found = 1;
4532 break;
4536 m_arMarkedWordLines.push_back(found);
4538 else
4539 m_arMarkedWordLines.push_back(0);
4541 else
4542 m_arMarkedWordLines.push_back(0);
4546 void CBaseView::BuildFindStringArray()
4548 int lineCount = GetLineCount();
4549 m_arFindStringLines.clear();
4550 m_arFindStringLines.reserve(lineCount);
4551 bool bDoit = !m_sFindText.IsEmpty();
4552 int s = 0;
4553 int e = 0;
4554 for (int i = 0; i < lineCount; ++i)
4556 if (bDoit)
4558 CString line = GetLineChars(i);
4560 if (!line.IsEmpty())
4562 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4564 case DIFFSTATE_EMPTY:
4565 m_arFindStringLines.push_back(0);
4566 break;
4567 case DIFFSTATE_UNKNOWN:
4568 case DIFFSTATE_NORMAL:
4569 case DIFFSTATE_FILTEREDDIFF:
4570 if (m_bLimitToDiff)
4572 m_arFindStringLines.push_back(0);
4573 break;
4575 case DIFFSTATE_REMOVED:
4576 case DIFFSTATE_REMOVEDWHITESPACE:
4577 case DIFFSTATE_ADDED:
4578 case DIFFSTATE_ADDEDWHITESPACE:
4579 case DIFFSTATE_WHITESPACE:
4580 case DIFFSTATE_WHITESPACE_DIFF:
4581 case DIFFSTATE_CONFLICTED:
4582 case DIFFSTATE_CONFLICTED_IGNORED:
4583 case DIFFSTATE_CONFLICTADDED:
4584 case DIFFSTATE_CONFLICTEMPTY:
4585 case DIFFSTATE_CONFLICTRESOLVED:
4586 case DIFFSTATE_IDENTICALREMOVED:
4587 case DIFFSTATE_IDENTICALADDED:
4588 case DIFFSTATE_THEIRSREMOVED:
4589 case DIFFSTATE_THEIRSADDED:
4590 case DIFFSTATE_YOURSREMOVED:
4591 case DIFFSTATE_YOURSADDED:
4592 case DIFFSTATE_EDITED:
4594 if (!m_bMatchCase)
4595 line = line.MakeLower();
4596 s = 0;
4597 e = 0;
4598 int match = 0;
4599 while (StringFound(line, SearchNext, s, e))
4601 match++;
4602 s = e;
4603 e = 0;
4605 m_arFindStringLines.push_back(match);
4606 break;
4608 default:
4609 m_arFindStringLines.push_back(0);
4612 else
4613 m_arFindStringLines.push_back(0);
4615 else
4616 m_arFindStringLines.push_back(0);
4618 UpdateLocator();
4621 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4623 if (!m_bShowInlineDiff)
4624 return false;
4625 if (m_pwndBottom && !(m_pwndBottom->IsHidden()))
4626 return false;
4628 if (!m_pViewData || m_pViewData->GetCount() <= nViewLine)
4629 return false;
4630 const CString &sLine = m_pViewData->GetLine(nViewLine);
4631 if (sLine.IsEmpty())
4632 return false;
4634 CheckOtherView();
4635 if (!m_pOtherViewData)
4636 return false;
4638 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4639 if (sDiffLine.IsEmpty())
4640 return false;
4642 svn_diff_t* diff = nullptr;
4643 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4644 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4645 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4646 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4647 return false;
4649 size_t lineoffset = 0;
4650 size_t position = 0;
4651 while (diff)
4653 if (this == m_pwndRight)
4655 apr_off_t nTmp = diff->modified_length;
4656 diff->modified_length = diff->original_length;
4657 diff->original_length = nTmp;
4659 nTmp = diff->modified_start;
4660 diff->modified_start = diff->original_start;
4661 diff->original_start = nTmp;
4663 apr_off_t len = diff->original_length;
4664 size_t oldpos = position;
4666 for (apr_off_t i = 0; i < len; ++i)
4668 position += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4669 lineoffset++;
4672 if (diff->type == svn_diff__type_diff_modified)
4674 inlineDiffPos p;
4675 p.start = oldpos;
4676 p.end = position;
4677 positions.push_back(p);
4680 diff = diff->next;
4683 return !positions.empty();
4686 void CBaseView::OnNavigateNextinlinediff()
4688 int nX;
4689 if (GetNextInlineDiff(nX))
4691 POINT ptCaretViewPos = GetCaretViewPosition();
4692 ptCaretViewPos.x = nX;
4693 SetCaretAndGoalViewPosition(ptCaretViewPos);
4694 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4695 EnsureCaretVisible();
4699 void CBaseView::OnNavigatePrevinlinediff()
4701 int nX;
4702 if (GetPrevInlineDiff(nX))
4704 POINT ptCaretViewPos = GetCaretViewPosition();
4705 ptCaretViewPos.x = nX;
4706 SetCaretAndGoalViewPosition(ptCaretViewPos);
4707 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4708 EnsureCaretVisible();
4712 bool CBaseView::HasNextInlineDiff()
4714 int nPos;
4715 return GetNextInlineDiff(nPos);
4718 bool CBaseView::GetNextInlineDiff(int & nPos)
4720 POINT ptCaretViewPos = GetCaretViewPosition();
4721 std::vector<inlineDiffPos> positions;
4722 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4724 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4726 if (it->start > ptCaretViewPos.x)
4728 nPos = (LONG)it->start;
4729 return true;
4731 if (it->end > ptCaretViewPos.x)
4733 nPos = (LONG)it->end;
4734 return true;
4738 return false;
4741 bool CBaseView::HasPrevInlineDiff()
4743 int nPos;
4744 return GetPrevInlineDiff(nPos);
4747 bool CBaseView::GetPrevInlineDiff(int & nPos)
4749 POINT ptCaretViewPos = GetCaretViewPosition();
4750 std::vector<inlineDiffPos> positions;
4751 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4753 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4755 if ( it->end < ptCaretViewPos.x)
4757 nPos = (LONG)it->end;
4758 return true;
4760 if ( it->start < ptCaretViewPos.x)
4762 nPos = (LONG)it->start;
4763 return true;
4767 return false;
4770 CBaseView * CBaseView::GetFirstGoodView()
4772 if (IsViewGood(m_pwndLeft))
4773 return m_pwndLeft;
4774 if (IsViewGood(m_pwndRight))
4775 return m_pwndRight;
4776 if (IsViewGood(m_pwndBottom))
4777 return m_pwndBottom;
4778 return nullptr;
4781 void CBaseView::BuildAllScreen2ViewVector()
4783 CBaseView * p_pwndView = GetFirstGoodView();
4784 if (p_pwndView)
4786 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4790 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4792 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4795 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4797 CBaseView * p_pwndView = GetFirstGoodView();
4798 if (p_pwndView)
4800 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4804 void CBaseView::UpdateViewLineNumbers()
4806 int nLineNumber = 0;
4807 int nViewLineCount = GetViewCount();
4808 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4810 int oldLine = (int)GetViewLineNumber(nViewLine);
4811 if (oldLine >= 0)
4812 SetViewLineNumber(nViewLine, nLineNumber++);
4814 m_nDigits = 0;
4817 int CBaseView::CleanEmptyLines()
4819 int nRemovedCount = 0;
4820 int nViewLineCount = GetViewCount();
4821 bool bCheckLeft = IsViewGood(m_pwndLeft);
4822 bool bCheckRight = IsViewGood(m_pwndRight);
4823 bool bCheckBottom = IsViewGood(m_pwndBottom);
4824 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4826 bool bAllEmpty = true;
4827 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4828 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4829 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4830 if (bAllEmpty)
4832 if (bCheckLeft)
4834 m_pwndLeft->RemoveViewData(nViewLine);
4836 if (bCheckRight)
4838 m_pwndRight->RemoveViewData(nViewLine);
4840 if (bCheckBottom)
4842 m_pwndBottom->RemoveViewData(nViewLine);
4844 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4846 SaveUndoStep();
4848 nViewLineCount--;
4849 nRemovedCount++;
4850 continue;
4852 nViewLine++;
4854 return nRemovedCount;
4857 int CBaseView::FindScreenLineForViewLine( int viewLine )
4859 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4862 int CBaseView::CountMultiLines( int nViewLine )
4864 if (m_ScreenedViewLine.empty())
4865 return 0; // in case the view is completely empty
4867 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4869 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4871 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4874 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4876 TScreenedViewLine oScreenedLine;
4877 // tokenize string
4878 int prevpos = 0;
4879 int pos = 0;
4880 while ((pos = multiline.Find('\n', pos)) >= 0)
4882 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4883 pos++;
4884 prevpos = pos;
4886 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4887 oScreenedLine.bSublinesSet = true;
4888 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4890 return CountMultiLines(nViewLine);
4893 /// prepare inline diff cache
4894 LineColors & CBaseView::GetLineColors(int nViewLine)
4896 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4898 if (m_bWhitespaceInlineDiffs)
4900 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4901 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4903 else
4905 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4906 return m_ScreenedViewLine[nViewLine].lineColors;
4909 LineColors oLineColors;
4910 // set main line color
4911 COLORREF crBkgnd, crText;
4912 DiffStates diffState = m_pViewData->GetState(nViewLine);
4913 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4914 oLineColors.SetColor(0, crText, crBkgnd);
4916 do {
4917 if (!m_bShowInlineDiff)
4918 break;
4920 if (((diffState == DIFFSTATE_NORMAL) || (diffState == DIFFSTATE_FILTEREDDIFF)) && (!m_bWhitespaceInlineDiffs))
4921 break;
4923 CString sLine = GetViewLineChars(nViewLine);
4924 if (sLine.IsEmpty())
4925 break;
4926 CString sDiffLine;
4927 if (!m_pOtherView)
4929 switch (diffState)
4931 case DIFFSTATE_ADDED:
4933 if ((nViewLine > 0) && (m_pViewData->GetState(nViewLine - 1) == DIFFSTATE_REMOVED))
4934 sDiffLine = GetViewLineChars(nViewLine - 1);
4936 break;
4937 case DIFFSTATE_REMOVED:
4939 if (((nViewLine + 1) < m_pViewData->GetCount()) && (m_pViewData->GetState(nViewLine + 1) == DIFFSTATE_ADDED))
4940 sDiffLine = GetViewLineChars(nViewLine + 1);
4942 break;
4945 else
4946 sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4947 if (sDiffLine.IsEmpty())
4948 break;
4950 svn_diff_t* diff = nullptr;
4951 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4952 break;
4953 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4954 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4955 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4956 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4957 break;
4959 int lineoffset = 0;
4960 int nTextStartOffset = 0;
4961 std::map<int, COLORREF> removedPositions;
4962 while (diff)
4964 if (this == m_pwndRight)
4966 apr_off_t nTmp = diff->modified_length;
4967 diff->modified_length = diff->original_length;
4968 diff->original_length = nTmp;
4970 nTmp = diff->modified_start;
4971 diff->modified_start = diff->original_start;
4972 diff->original_start = nTmp;
4974 apr_off_t len = diff->original_length;
4976 size_t nTextLength = 0;
4977 for (int i = 0; i < len; ++i)
4979 nTextLength += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4980 lineoffset++;
4982 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4984 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4985 if ((m_bShowInlineDiff)&&(bInlineDiff))
4987 crBkgnd = InlineViewLineDiffColor(nViewLine);
4989 else if (m_pOtherView)
4991 crBkgnd = m_ModifiedBk;
4994 if (len < diff->modified_length)
4996 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4998 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
5000 nTextStartOffset += (int)nTextLength;
5001 diff = diff->next;
5003 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
5005 oLineColors.AddShotColor(it->first, it->second);
5007 } while (false); // error catch
5009 if (!m_bWhitespaceInlineDiffs)
5011 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
5012 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
5014 else
5016 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
5017 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
5020 return GetLineColors(nViewLine);
5023 void CBaseView::OnEditSelectall()
5025 if (!m_pViewData)
5026 return;
5027 int nLastViewLine = m_pViewData->GetCount()-1;
5028 if (nLastViewLine < 0)
5029 return;
5030 SetupAllViewSelection(0, nLastViewLine);
5032 CString sLine = GetViewLineChars(nLastViewLine);
5033 m_ptSelectionViewPosStart = SetupPoint(0, 0);
5034 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
5035 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
5037 UpdateWindow();
5040 void CBaseView::FilterWhitespaces(CString& first, CString& second)
5042 FilterWhitespaces(first);
5043 FilterWhitespaces(second);
5046 void CBaseView::FilterWhitespaces(CString& line)
5048 line.Remove(' ');
5049 line.Remove('\t');
5050 line.Remove('\r');
5051 line.Remove('\n');
5054 int CBaseView::GetButtonEventLineIndex(const POINT& point)
5056 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
5057 int nEventLine = nLineFromTop + m_nTopLine;
5058 nEventLine--; //we need the index
5059 return nEventLine;
5063 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
5065 if (RelayTrippleClick(pMsg))
5066 return TRUE;
5067 return CView::PreTranslateMessage(pMsg);
5071 void CBaseView::ResetUndoStep()
5073 m_AllState.Clear();
5076 void CBaseView::SaveUndoStep()
5078 if (!m_AllState.IsEmpty())
5080 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
5082 ResetUndoStep();
5085 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
5087 m_pState->addedlines.push_back(index);
5088 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
5091 void CBaseView::InsertViewData( int index, const viewdata& data )
5093 m_pState->addedlines.push_back(index);
5094 m_pViewData->InsertData(index, data);
5097 void CBaseView::RemoveViewData( int index )
5099 m_pState->removedlines[index] = m_pViewData->GetData(index);
5100 m_pViewData->RemoveData(index);
5103 void CBaseView::SetViewData( int index, const viewdata& data )
5105 m_pState->replacedlines[index] = m_pViewData->GetData(index);
5106 m_pViewData->SetData(index, data);
5109 void CBaseView::SetViewState( int index, DiffStates state )
5111 m_pState->linestates[index] = m_pViewData->GetState(index);
5112 m_pViewData->SetState(index, state);
5115 void CBaseView::SetViewLine( int index, const CString& sLine )
5117 m_pState->difflines[index] = m_pViewData->GetLine(index);
5118 m_pViewData->SetLine(index, sLine);
5121 void CBaseView::SetViewLineNumber( int index, int linenumber )
5123 int oldLineNumber = m_pViewData->GetLineNumber(index);
5124 if (oldLineNumber != linenumber) {
5125 m_pState->linelines[index] = oldLineNumber;
5126 m_pViewData->SetLineNumber(index, linenumber);
5130 void CBaseView::SetViewLineEnding( int index, EOL ending )
5132 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
5133 m_pViewData->SetLineEnding(index, ending);
5136 void CBaseView::SetViewMarked( int index, bool marked )
5138 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5139 m_pViewData->SetMarked(index, marked);
5143 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5145 if (HasSelection())
5147 start = m_nSelViewBlockStart;
5148 end = m_nSelViewBlockEnd;
5149 return true;
5151 return false;
5154 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5156 RebuildIfNecessary();
5157 if ((size() <= screenLine) || (screenLine < 0))
5158 return 0;
5159 return m_Screen2View[screenLine].nViewLine;
5162 int CBaseView::Screen2View::size()
5164 RebuildIfNecessary();
5165 return (int)m_Screen2View.size();
5168 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5170 RebuildIfNecessary();
5171 if (size() <= screenLine)
5172 return 0;
5173 return m_Screen2View[screenLine].nViewSubLine;
5176 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5178 RebuildIfNecessary();
5179 return m_Screen2View[screenLine];
5183 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5185 void CBaseView::Screen2View::RebuildIfNecessary()
5187 if (!m_pViewData)
5188 return; // rebuild not necessary
5190 FixScreenedCacheSize(m_pwndLeft);
5191 FixScreenedCacheSize(m_pwndRight);
5192 FixScreenedCacheSize(m_pwndBottom);
5193 if (!m_bFull)
5195 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5197 ResetScreenedViewLineCache(m_pwndLeft, *it);
5198 ResetScreenedViewLineCache(m_pwndRight, *it);
5199 ResetScreenedViewLineCache(m_pwndBottom, *it);
5202 else
5204 ResetScreenedViewLineCache(m_pwndLeft);
5205 ResetScreenedViewLineCache(m_pwndRight);
5206 ResetScreenedViewLineCache(m_pwndBottom);
5208 m_RebuildRanges.clear();
5209 m_bFull = false;
5211 size_t OldSize = m_Screen2View.size();
5212 m_Screen2View.clear();
5213 m_Screen2View.reserve(OldSize); // guess same size
5214 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5216 if (m_pMainFrame->m_bCollapsed)
5218 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5219 ++i;
5220 if (!(i < m_pViewData->GetCount()))
5221 break;
5223 TScreenLineInfo oLineInfo;
5224 oLineInfo.nViewLine = i;
5225 oLineInfo.nViewSubLine = -1; // no wrap
5226 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5228 int nMaxLines = 0;
5229 if (IsLeftViewGood())
5230 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5231 if (IsRightViewGood())
5232 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5233 if (IsBottomViewGood())
5234 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5235 for (int l = 0; l < (nMaxLines-1); ++l)
5237 oLineInfo.nViewSubLine++;
5238 m_Screen2View.push_back(oLineInfo);
5240 oLineInfo.nViewSubLine++;
5242 m_Screen2View.push_back(oLineInfo);
5244 m_pViewData = nullptr;
5246 if (IsLeftViewGood())
5247 m_pwndLeft->BuildMarkedWordArray();
5248 if (IsRightViewGood())
5249 m_pwndRight->BuildMarkedWordArray();
5250 if (IsBottomViewGood())
5251 m_pwndBottom->BuildMarkedWordArray();
5252 UpdateLocator();
5253 RecalcAllVertScrollBars();
5254 RecalcAllHorzScrollBars();
5257 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5259 RebuildIfNecessary();
5261 int nScreenLineCount = (int)m_Screen2View.size();
5263 int nPos = 0;
5264 if (nScreenLineCount>16)
5266 // for enough long data search for last screen
5267 // with viewline less than one we are looking for
5268 // use approximate method (based on) binary search using asymmetric start point
5269 // in form 2**n (determined as MSB of length) to go around division and rounding;
5270 // this effectively looks for bit values from MSB to LSB
5272 int nTestBit;
5273 //GetMostSignificantBitValue
5274 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5275 nTestBit = nScreenLineCount;
5276 nTestBit |= nTestBit>>1;
5277 nTestBit |= nTestBit>>2;
5278 nTestBit |= nTestBit>>4;
5279 nTestBit |= nTestBit>>8;
5280 nTestBit |= nTestBit>>16;
5281 nTestBit ^= (nTestBit>>1);
5283 while (nTestBit)
5285 int nTestPos = nPos | nTestBit;
5286 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5288 nPos = nTestPos;
5290 nTestBit >>= 1;
5293 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5295 nPos++;
5298 return nPos;
5301 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5302 m_bFull = true;
5304 m_pViewData = pViewData;
5307 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5309 if (m_bFull)
5310 return;
5312 m_pViewData = pViewData;
5314 TRebuildRange Range;
5315 Range.FirstViewLine=nFirstViewLine;
5316 Range.LastViewLine=nLastViewLine;
5317 m_RebuildRanges.push_back(Range);
5320 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5322 if (!IsViewGood(pwndView))
5324 return false;
5326 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5327 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5328 if (nOldSize == nViewCount)
5330 return false;
5332 pwndView->m_ScreenedViewLine.resize(nViewCount);
5333 return true;
5336 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5338 if (!IsViewGood(pwndView))
5340 return false;
5342 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5343 ResetScreenedViewLineCache(pwndView, Range);
5344 return true;
5347 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5349 if (!IsViewGood(pwndView))
5351 return false;
5353 if (Range.LastViewLine == -1)
5355 return false;
5357 ASSERT(Range.FirstViewLine >= 0);
5358 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5359 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5361 pwndView->m_ScreenedViewLine[i].Clear();
5363 return false;
5366 void CBaseView::WrapChanged()
5368 m_nMaxLineLength = -1;
5369 m_nOffsetChar = 0;
5372 void CBaseView::OnEditFind()
5374 if (m_pFindDialog)
5375 return;
5377 int id = 0;
5378 if (this == m_pwndLeft)
5379 id = 1;
5380 if (this == m_pwndRight)
5381 id = 2;
5382 if (this == m_pwndBottom)
5383 id = 3;
5385 m_pFindDialog = new CFindDlg(this);
5386 m_pFindDialog->Create(this, id);
5388 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5389 m_pFindDialog->SetReadonly(m_bReadonly);
5392 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5394 ASSERT(m_pFindDialog != nullptr);
5396 if (m_pFindDialog->IsTerminating())
5398 // invalidate the handle identifying the dialog box.
5399 m_pFindDialog = nullptr;
5400 return 0;
5403 if(m_pFindDialog->FindNext())
5405 //read data from dialog
5406 m_sFindText = m_pFindDialog->GetFindString();
5407 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5408 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5409 m_bWholeWord = m_pFindDialog->WholeWord();
5411 if (!m_bMatchCase)
5412 m_sFindText = m_sFindText.MakeLower();
5414 BuildFindStringArray();
5415 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5417 if (m_pFindDialog->SearchUp())
5418 OnEditFindprev();
5419 else
5420 OnEditFindnext();
5422 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5424 size_t count = 0;
5425 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5426 count += m_arFindStringLines[i];
5427 CString matches;
5428 matches.Format(IDS_FIND_COUNT, count);
5429 m_pFindDialog->SetStatusText(matches);
5431 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5433 if (!IsWritable())
5434 return 0;
5435 bool bFound = false;
5436 if (m_pFindDialog->SearchUp())
5437 bFound = Search(SearchPrevious, true, true, false);
5438 else
5439 bFound = Search(SearchNext, true, true, false);
5440 if (bFound)
5442 CString sReplaceText = m_pFindDialog->GetReplaceString();
5443 CUndo::GetInstance().BeginGrouping();
5444 RemoveSelectedText();
5445 InsertText(sReplaceText);
5446 CUndo::GetInstance().EndGrouping();
5450 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5452 if (!IsWritable())
5453 return 0;
5454 bool bFound = false;
5455 int replaceCount = 0;
5456 POINT lastPoint = m_ptSelectionViewPosStart;
5457 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5458 CUndo::GetInstance().BeginGrouping();
5461 bFound = Search(SearchNext, true, false, true);
5462 if (bFound)
5464 CString sReplaceText = m_pFindDialog->GetReplaceString();
5465 RemoveSelectedText();
5466 InsertText(sReplaceText);
5467 ++replaceCount;
5469 } while (bFound);
5470 CUndo::GetInstance().EndGrouping();
5471 if (replaceCount == 0)
5472 m_ptSelectionViewPosStart = lastPoint;
5473 CString message;
5474 message.Format(IDS_FIND_REPLACED, replaceCount);
5475 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5479 return 0;
5482 void CBaseView::OnEditFindnextStart()
5484 if (!m_pViewData)
5485 return;
5486 if (HasTextSelection())
5488 m_sFindText = GetSelectedText();
5489 m_bMatchCase = false;
5490 m_bLimitToDiff = false;
5491 m_bWholeWord = false;
5492 m_sFindText = m_sFindText.MakeLower();
5494 BuildFindStringArray();
5495 OnEditFindnext();
5497 else
5499 m_sFindText.Empty();
5500 BuildFindStringArray();
5504 void CBaseView::OnEditFindprevStart()
5506 if (!m_pViewData)
5507 return;
5508 if (HasTextSelection())
5510 m_sFindText = GetSelectedText();
5511 m_bMatchCase = false;
5512 m_bLimitToDiff = false;
5513 m_bWholeWord = false;
5514 m_sFindText = m_sFindText.MakeLower();
5516 BuildFindStringArray();
5517 OnEditFindprev();
5519 else
5521 m_sFindText.Empty();
5522 BuildFindStringArray();
5526 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5528 if (srchDir == SearchPrevious)
5530 int laststart = -1;
5531 int laststart2 = -1;
5534 laststart2 = laststart;
5535 laststart = str.Find(m_sFindText, laststart + 1);
5536 } while (laststart >= 0 && laststart < start);
5537 start = laststart2;
5539 else
5540 start = str.Find(m_sFindText, start);
5541 end = start + m_sFindText.GetLength();
5542 bool bStringFound = (start >= 0);
5543 if (bStringFound && m_bWholeWord)
5545 if (start)
5546 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5548 if (bStringFound)
5550 if (str.GetLength() > end)
5551 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5554 return bStringFound;
5557 void CBaseView::OnEditFindprev()
5559 Search(SearchPrevious, false, true, false);
5562 void CBaseView::OnEditFindnext()
5564 Search(SearchNext, false, true, false);
5567 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5569 if (m_sFindText.IsEmpty())
5570 return false;
5571 if(!m_pViewData)
5572 return false;
5574 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5575 POINT end;
5576 end.y = m_pViewData->GetCount()-1;
5577 if (end.y < 0)
5578 return false;
5580 if (srchDir==SearchNext)
5581 end.x = GetViewLineLength(end.y);
5582 else
5584 end.x = m_ptSelectionViewPosStart.x;
5585 start.x = 0;
5588 if (!HasTextSelection())
5590 start.y = m_ptCaretViewPos.y;
5591 if (srchDir==SearchNext)
5592 start.x = m_ptCaretViewPos.x;
5593 else
5595 start.x = 0;
5596 end.x = m_ptCaretViewPos.x;
5599 CString sSelectedText;
5600 int startline = -1;
5601 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5603 if (nViewLine < 0)
5605 if (stopEof)
5606 return false;
5607 nViewLine = m_pViewData->GetCount()-1;
5608 startline = start.y;
5609 if (flashIfNotFound)
5611 if (m_pFindDialog)
5612 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5613 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5616 if (nViewLine > end.y)
5618 if (stopEof)
5619 return false;
5620 nViewLine = 0;
5621 startline = start.y;
5622 if (flashIfNotFound)
5624 if (m_pFindDialog)
5625 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5626 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5629 switch (m_pViewData->GetState(nViewLine))
5631 case DIFFSTATE_EMPTY:
5632 break;
5633 case DIFFSTATE_UNKNOWN:
5634 case DIFFSTATE_NORMAL:
5635 case DIFFSTATE_FILTEREDDIFF:
5636 if (m_bLimitToDiff)
5637 break;
5638 case DIFFSTATE_REMOVED:
5639 case DIFFSTATE_REMOVEDWHITESPACE:
5640 case DIFFSTATE_ADDED:
5641 case DIFFSTATE_ADDEDWHITESPACE:
5642 case DIFFSTATE_WHITESPACE:
5643 case DIFFSTATE_WHITESPACE_DIFF:
5644 case DIFFSTATE_CONFLICTED:
5645 case DIFFSTATE_CONFLICTED_IGNORED:
5646 case DIFFSTATE_CONFLICTADDED:
5647 case DIFFSTATE_CONFLICTEMPTY:
5648 case DIFFSTATE_CONFLICTRESOLVED:
5649 case DIFFSTATE_IDENTICALREMOVED:
5650 case DIFFSTATE_IDENTICALADDED:
5651 case DIFFSTATE_THEIRSREMOVED:
5652 case DIFFSTATE_THEIRSADDED:
5653 case DIFFSTATE_YOURSREMOVED:
5654 case DIFFSTATE_YOURSADDED:
5655 case DIFFSTATE_EDITED:
5657 sSelectedText = GetViewLineChars(nViewLine);
5658 if (nViewLine == start.y && startline < 0)
5659 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5660 if (!m_bMatchCase)
5661 sSelectedText = sSelectedText.MakeLower();
5662 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5663 int endfound = 0;
5664 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5666 HighlightViewLines(nViewLine, nViewLine);
5667 m_ptSelectionViewPosStart.x = startfound;
5668 m_ptSelectionViewPosEnd.x = endfound;
5669 if (nViewLine == start.y && startline < 0)
5671 m_ptSelectionViewPosStart.x += start.x;
5672 m_ptSelectionViewPosEnd.x += start.x;
5674 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5675 m_ptSelectionViewPosStart.y = nViewLine;
5676 m_ptSelectionViewPosEnd.y = nViewLine;
5677 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5678 UpdateViewsCaretPosition();
5679 EnsureCaretVisible();
5680 Invalidate();
5681 return true;
5684 break;
5687 if (startline >= 0)
5689 if (nViewLine == startline)
5691 if (flashIfNotFound)
5693 CString message;
5694 message.Format(IDS_FIND_NOTFOUND, (LPCTSTR)m_sFindText);
5695 if (m_pFindDialog)
5696 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5697 ::MessageBeep(0xFFFFFFFF);
5698 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5700 break;
5704 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5705 return false;
5708 CString CBaseView::GetSelectedText() const
5710 CString sSelectedText;
5711 POINT start = m_ptSelectionViewPosStart;
5712 POINT end = m_ptSelectionViewPosEnd;
5713 if (!HasTextSelection())
5715 if (!HasSelection())
5716 return sSelectedText;
5717 start.y = m_nSelViewBlockStart;
5718 start.x = 0;
5719 end.y = m_nSelViewBlockEnd;
5720 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5722 if (!m_pViewData)
5723 return sSelectedText;
5724 // first store the selected lines in one CString
5725 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5727 switch (m_pViewData->GetState(nViewLine))
5729 case DIFFSTATE_EMPTY:
5730 break;
5731 case DIFFSTATE_UNKNOWN:
5732 case DIFFSTATE_NORMAL:
5733 case DIFFSTATE_REMOVED:
5734 case DIFFSTATE_REMOVEDWHITESPACE:
5735 case DIFFSTATE_ADDED:
5736 case DIFFSTATE_ADDEDWHITESPACE:
5737 case DIFFSTATE_WHITESPACE:
5738 case DIFFSTATE_WHITESPACE_DIFF:
5739 case DIFFSTATE_CONFLICTED:
5740 case DIFFSTATE_CONFLICTED_IGNORED:
5741 case DIFFSTATE_CONFLICTADDED:
5742 case DIFFSTATE_CONFLICTEMPTY:
5743 case DIFFSTATE_CONFLICTRESOLVED:
5744 case DIFFSTATE_IDENTICALREMOVED:
5745 case DIFFSTATE_IDENTICALADDED:
5746 case DIFFSTATE_THEIRSREMOVED:
5747 case DIFFSTATE_THEIRSADDED:
5748 case DIFFSTATE_YOURSREMOVED:
5749 case DIFFSTATE_YOURSADDED:
5750 case DIFFSTATE_EDITED:
5751 case DIFFSTATE_FILTEREDDIFF:
5752 sSelectedText += GetViewLineChars(nViewLine);
5753 sSelectedText += L"\r\n";
5754 break;
5757 // remove the non-selected chars from the first line, last line and last \r\n
5758 int nLeftCut = start.x;
5759 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5760 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5761 return sSelectedText;
5764 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods, bool& hasFilteredMods)
5766 hasMods = false;
5767 hasConflicts = false;
5768 hasWhitespaceMods = false;
5769 hasFilteredMods = false;
5771 if (m_pViewData)
5773 for (int i=0; i<m_pViewData->GetCount(); i++)
5775 DiffStates state = m_pViewData->GetState(i);
5776 switch (state)
5778 case DIFFSTATE_ADDED:
5779 case DIFFSTATE_IDENTICALADDED:
5780 case DIFFSTATE_THEIRSADDED:
5781 case DIFFSTATE_YOURSADDED:
5782 case DIFFSTATE_CONFLICTADDED:
5783 case DIFFSTATE_IDENTICALREMOVED:
5784 case DIFFSTATE_REMOVED:
5785 case DIFFSTATE_THEIRSREMOVED:
5786 case DIFFSTATE_YOURSREMOVED:
5787 case DIFFSTATE_EMPTY:
5788 hasMods = true;
5789 break;
5790 case DIFFSTATE_CONFLICTED:
5791 case DIFFSTATE_CONFLICTED_IGNORED:
5792 hasConflicts = true;
5793 break;
5794 case DIFFSTATE_REMOVEDWHITESPACE:
5795 case DIFFSTATE_ADDEDWHITESPACE:
5796 case DIFFSTATE_WHITESPACE:
5797 case DIFFSTATE_WHITESPACE_DIFF:
5798 hasWhitespaceMods = true;
5799 break;
5800 case DIFFSTATE_FILTEREDDIFF:
5801 hasFilteredMods = true;
5802 break;
5808 void CBaseView::OnEditGotoline()
5810 if (!m_pViewData)
5811 return;
5812 // find the last and first line number
5813 int nViewLineCount = m_pViewData->GetCount();
5815 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5816 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5818 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5819 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5821 break;
5824 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5826 return;
5828 nLastLineNumber++;
5829 int nFirstLineNumber=1; // first is always 1
5831 CString sText;
5832 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5834 CGotoLineDlg dlg(this);
5835 dlg.SetLabel(sText);
5836 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5837 if (dlg.DoModal() == IDOK)
5839 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5841 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5843 HighlightViewLines(nViewLine, nViewLine);
5844 return;
5850 void CBaseView::OnToggleReadonly()
5852 if (IsReadonlyChangable()) {
5853 SetWritable(IsReadonly());
5857 int CBaseView::SaveFile(int nFlags)
5859 Invalidate();
5860 if (m_pViewData && m_pWorkingFile)
5862 CFileTextLines file;
5863 m_SaveParams.m_LineEndings = m_lineendings;
5864 m_SaveParams.m_UnicodeType = m_texttype;
5865 file.SetSaveParams(m_SaveParams);
5867 for (int i=0; i<m_pViewData->GetCount(); i++)
5869 //only copy non-removed lines
5870 DiffStates state = m_pViewData->GetState(i);
5871 switch (state)
5873 case DIFFSTATE_CONFLICTED:
5874 case DIFFSTATE_CONFLICTED_IGNORED:
5876 int first = i;
5877 int last = i;
5880 last++;
5881 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5882 file.Add(L"<<<<<<< .mine", EOL_NOENDING);
5883 for (int j=first; j<last; j++)
5885 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5887 file.Add(L"=======", EOL_NOENDING);
5888 for (int j=first; j<last; j++)
5890 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5892 file.Add(L">>>>>>> .theirs", EOL_NOENDING);
5893 i = last-1;
5895 break;
5896 case DIFFSTATE_EMPTY:
5897 break;
5898 case DIFFSTATE_CONFLICTEMPTY:
5899 case DIFFSTATE_IDENTICALREMOVED:
5900 case DIFFSTATE_REMOVED:
5901 case DIFFSTATE_THEIRSREMOVED:
5902 case DIFFSTATE_YOURSREMOVED:
5903 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5904 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5906 // do not save removed lines
5907 break;
5909 default:
5910 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5911 break;
5914 CString filename = m_pWorkingFile->GetFilename();
5915 if (m_pWorkingFile->IsReadonly())
5916 if (!CCommonAppUtils::FileOpenSave(filename, nullptr, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5917 return -1;
5918 if (!file.Save(filename))
5920 ::MessageBox(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
5921 return -1;
5923 m_pWorkingFile->SetFileName(filename);
5924 m_pWorkingFile->StoreFileAttributes();
5925 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5926 SetModified(FALSE);
5927 CUndo::GetInstance().MarkAsOriginalState(
5928 this == m_pwndLeft,
5929 this == m_pwndRight,
5930 this == m_pwndBottom);
5931 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5932 return 0;
5933 return file.GetCount();
5935 return 1;
5939 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5941 if (m_pWorkingFile)
5943 m_pWorkingFile->SetFileName(sFileName);
5944 return SaveFile(nFlags);
5946 return -1;
5950 EOL CBaseView::GetLineEndings()
5952 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5955 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5957 if (bHasMixedEols)
5959 return EOL_AUTOLINE; // mixed eols - hack value
5961 if (m_lineendings == EOL_AUTOLINE)
5963 return EOL_CRLF;
5965 return m_lineendings;
5968 void CBaseView::ReplaceLineEndings(EOL eEol)
5970 if (eEol == EOL_AUTOLINE)
5972 return;
5974 // set AUTOLINE
5975 m_lineendings = eEol;
5976 // replace all set EOLs
5977 // TODO store line endings and lineendings in undo
5978 //CUndo::BeginGrouping();
5979 for (int i = 0; i < GetViewCount(); ++i)
5981 if (IsLineEmpty(i))
5983 continue;
5985 EOL eLineEol = GetViewLineEnding(i);
5986 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5988 continue;
5990 SetViewLineEnding(i, eEol);
5992 //CUndo::EndGrouping();
5993 //CUndo::saveundostep;
5994 DocumentUpdated();
5995 SetModified();
5998 void CBaseView::SetLineEndingStyle(EOL eEol)
6000 m_lineendings = eEol;
6003 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
6005 if (m_texttype == eTextType)
6007 return;
6009 m_texttype = eTextType;
6010 DocumentUpdated();
6011 SetModified();
6014 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
6016 if (IsReadonly())
6017 return; // nothing to be changed in read-only view
6018 CEncodingDlg dlg;
6019 dlg.view.LoadString(nTextId);
6020 dlg.texttype = m_texttype;
6021 dlg.lineendings = GetLineEndings();
6022 if (dlg.DoModal() != IDOK)
6023 return;
6024 SetTextType(dlg.texttype);
6025 ReplaceLineEndings(dlg.lineendings);
6029 Replaces lines from source view to this
6031 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
6033 if (!IsViewGood(pwndView))
6034 return;
6035 if (!IsWritable())
6036 return;
6037 CUndo::GetInstance().BeginGrouping();
6039 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6041 bool skip = fnSkip(viewLine);
6042 if (skip)
6044 if (GetViewMarked(viewLine))
6045 SetViewMarked(viewLine, false);
6046 continue;
6048 viewdata line = pwndView->GetViewData(viewLine);
6049 if (line.ending != EOL_NOENDING)
6050 line.ending = m_lineendings;
6051 switch (line.state)
6053 case DIFFSTATE_CONFLICTEMPTY:
6054 case DIFFSTATE_UNKNOWN:
6055 line.state = DIFFSTATE_EMPTY;
6056 case DIFFSTATE_EMPTY:
6057 break;
6058 case DIFFSTATE_ADDED:
6059 case DIFFSTATE_CONFLICTADDED:
6060 case DIFFSTATE_CONFLICTED:
6061 case DIFFSTATE_CONFLICTED_IGNORED:
6062 case DIFFSTATE_IDENTICALADDED:
6063 case DIFFSTATE_THEIRSADDED:
6064 case DIFFSTATE_YOURSADDED:
6065 case DIFFSTATE_IDENTICALREMOVED:
6066 case DIFFSTATE_REMOVED:
6067 case DIFFSTATE_THEIRSREMOVED:
6068 case DIFFSTATE_YOURSREMOVED:
6069 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
6070 line.state = DIFFSTATE_NORMAL;
6071 case DIFFSTATE_NORMAL:
6072 break;
6073 default:
6074 break;
6076 bool marked = GetViewMarked(viewLine);
6077 SetViewData(viewLine, line);
6078 if (marked)
6079 SetViewMarked(viewLine, false);
6080 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
6082 // if this view is in ASCII and the other is not, we have to make sure that
6083 // the text we copy from the other view can actually be saved in ASCII encoding.
6084 // if not, we have to change this views encoding to the same encoding as the other view
6085 BOOL useDefault = FALSE;
6086 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, nullptr, 0, 0, &useDefault);
6087 if (useDefault) // a default char is required, so the char can not be saved as ASCII
6088 SetTextType(pwndView->GetTextType());
6091 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6092 // TODO: check if copied line is same as original one set modified only when differ
6093 SetModified();
6094 SaveUndoStep();
6096 int nRemovedLines = CleanEmptyLines();
6097 SaveUndoStep();
6098 //VerifyEols();
6099 // make sure all non empty line have EOL set but last
6100 // wrong can be last copied line(have eol, but no line under),
6101 // or old last line (line before copied block missing eol, but have line under)
6102 // we'll check all lines to be sure
6103 int nLine = GetViewCount();
6104 // check last line have no EOL set
6105 while (--nLine>=0)
6107 if (!IsViewLineEmpty(nLine))
6109 if (GetViewLineEnding(nLine) != EOL_NOENDING)
6111 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6112 // so next line is empty
6113 ASSERT(IsViewLineEmpty(nLine+1));
6114 // and we can turn it to normal empty line
6115 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
6117 break;
6120 // check all (nonlast) line have EOL set
6121 while (--nLine>=0)
6123 if (!IsViewLineEmpty(nLine))
6125 if (GetViewLineEnding(nLine) == EOL_NOENDING)
6127 SetViewLineEnding(nLine, m_lineendings);
6128 // in theory there should be only one line needing fix, but most of time we get over all anyway
6129 // break;
6133 SaveUndoStep();
6134 UpdateViewLineNumbers();
6135 SaveUndoStep();
6137 CUndo::GetInstance().EndGrouping();
6139 if (nRemovedLines!=0)
6141 // some lines are gone update selection
6142 ClearSelection();
6143 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6145 BuildAllScreen2ViewVector();
6146 pwndView->Invalidate();
6147 RefreshViews();
6150 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6152 if (!IsWritable())
6153 return;
6154 CUndo::GetInstance().BeginGrouping();
6156 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6157 SetViewMarked(viewLine, marked);
6159 SetModified();
6160 SaveUndoStep();
6161 CUndo::GetInstance().EndGrouping();
6163 BuildAllScreen2ViewVector();
6164 Invalidate();
6165 RefreshViews();
6168 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6170 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6171 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6174 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6176 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6177 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6180 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6182 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6183 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6186 int CBaseView::GetIndentCharsForLine(int x, int y)
6188 const int maxGuessLine = 100;
6189 int nTabMode = -1;
6190 const CString& line = GetViewLine(y);
6191 if (m_nTabMode & TABMODE_SMARTINDENT)
6193 // if the line contains one tab, use tabs
6194 // we can not test for spaces, since even if tabs are used,
6195 // spaces are used in a tabified file for alignment.
6196 if (line.Find(L'\t') >= 0)
6197 nTabMode = 0; // use tabs
6198 else if (line.GetLength() > m_nTabSize)
6199 nTabMode = 1; // use spaces
6201 if (m_nTabMode & TABMODE_SMARTINDENT)
6203 // detect lines nearby
6204 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6206 bool above = i > 0 && i >= y - maxGuessLine;
6207 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6208 if (!(above || below))
6209 break;
6210 auto ac = GetViewLine(i);
6211 auto bc = GetViewLine(j);
6212 if ((ac.Find(L'\t') >= 0) || (bc.Find(L'\t') >= 0))
6214 nTabMode = 0;
6215 break;
6217 else if ((ac.GetLength() > m_nTabSize) && (bc.GetLength() > m_nTabSize))
6219 nTabMode = 1;
6220 break;
6225 else
6226 nTabMode = m_nTabMode & TABMODE_USESPACES;
6228 if (nTabMode > 0)
6230 // use spaces
6231 x = CountExpandedChars(line, x);
6232 return (m_nTabSize - (x % m_nTabSize));
6235 // use tab
6236 return 0;
6239 void CBaseView::AddIndentationForSelectedBlock()
6241 bool bModified = false;
6242 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6244 // skip the line if no character is selected in the last selected line
6245 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6247 continue;
6249 // skip empty lines
6250 if (IsLineEmpty(nViewLine))
6252 continue;
6254 const CString &sLine = GetViewLine(nViewLine);
6255 CString sTemp = sLine;
6256 if (sTemp.Trim().IsEmpty())
6258 // skip empty and whitechar only lines
6259 continue;
6261 // add tab to line start (alternatively m_nTabSize spaces can be used)
6262 CString tabStr;
6263 int indentChars = GetIndentCharsForLine(0, nViewLine);
6264 tabStr = indentChars > 0 ? CString(L' ', indentChars) : L"\t";
6265 SetViewLine(nViewLine, tabStr + sLine);
6266 bModified = true;
6268 if (bModified)
6270 SetModified();
6271 SaveUndoStep();
6272 BuildAllScreen2ViewVector();
6276 void CBaseView::RemoveIndentationForSelectedBlock()
6278 bool bModified = false;
6279 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6281 // skip the line if no character is selected in the last selected line
6282 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6284 continue;
6286 // skip empty lines
6287 if (IsLineEmpty(nViewLine))
6289 continue;
6291 CString sLine = GetViewLine(nViewLine);
6292 // remove up to n spaces from line start
6293 // and one tab (if less then n spaces was removed)
6294 int nPos = 0;
6295 while (nPos<m_nTabSize)
6297 switch (sLine[nPos])
6299 case ' ':
6300 nPos++;
6301 continue;
6302 case '\t':
6303 nPos++;
6305 break;
6307 if (nPos>0)
6309 sLine.Delete(0, nPos);
6310 SetViewLine(nViewLine, sLine);
6311 bModified = true;
6314 if (bModified)
6316 SetModified();
6317 SaveUndoStep();
6318 BuildAllScreen2ViewVector();
6323 there are two possible versions
6324 - convert tabs to spaces only in front of text (implemented)
6325 - convert all tabs to spaces
6327 void CBaseView::ConvertTabToSpaces()
6329 bool bModified = false;
6330 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6332 if (IsLineEmpty(nViewLine))
6334 continue;
6336 const CString &sLine = GetViewLine(nViewLine);
6337 bool bTabToConvertFound = false;
6338 int nPosIn = 0;
6339 int nPosOut = 0;
6340 while (nPosIn<sLine.GetLength())
6342 switch (sLine[nPosIn])
6344 case ' ':
6345 nPosIn++;
6346 nPosOut++;
6347 continue;
6348 case '\t':
6349 nPosIn++;
6350 bTabToConvertFound = true;
6351 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6352 continue;
6354 break;
6356 if (bTabToConvertFound)
6358 CString sLineNew = sLine;
6359 sLineNew.Delete(0, nPosIn);
6360 sLineNew = CString(' ', nPosOut) + sLineNew;
6361 SetViewLine(nViewLine, sLineNew);
6362 bModified = true;
6365 if (bModified)
6367 SetModified();
6368 SaveUndoStep();
6369 BuildAllScreen2ViewVector();
6374 there are two possible version
6375 - convert spaces to tabs only in front of text (implemented)
6376 - convert all spaces to tabs
6378 void CBaseView::Tabularize()
6380 bool bModified = false;
6381 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6383 if (IsLineEmpty(nViewLine))
6385 continue;
6387 const CString &sLine = GetViewLine(nViewLine);
6388 int nDel = 0;
6389 int nTabCount = 0; // total tabs to be used
6390 int nSpaceCount = 0; // number of spaces in tab size run
6391 int nPos = 0;
6392 while (nPos<sLine.GetLength())
6394 switch (sLine[nPos++])
6396 case ' ':
6397 //bSpace = true;
6398 if (++nSpaceCount < m_nTabSize)
6400 continue;
6402 case '\t':
6403 nTabCount++;
6404 nSpaceCount = 0;
6405 nDel = nPos;
6406 continue;
6408 break;
6410 if (nDel > 0)
6412 CString sLineNew = sLine;
6413 sLineNew.Delete(0, nDel);
6414 sLineNew = CString('\t', nTabCount) + sLineNew;
6415 if (sLine!=sLineNew)
6417 SetViewLine(nViewLine, sLineNew);
6418 bModified = true;
6422 if (bModified)
6424 SetModified();
6425 SaveUndoStep();
6426 BuildAllScreen2ViewVector();
6430 void CBaseView::RemoveTrailWhiteChars()
6432 bool bModified = false;
6433 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6435 if (IsLineEmpty(nViewLine))
6437 continue;
6439 const CString &sLine = GetViewLine(nViewLine);
6440 CString sLineNew = sLine;
6441 sLineNew.TrimRight();
6442 if (sLine.GetLength()!=sLineNew.GetLength())
6444 SetViewLine(nViewLine, sLineNew);
6445 bModified = true;
6448 if (bModified)
6450 SetModified();
6451 SaveUndoStep();
6452 BuildAllScreen2ViewVector();
6456 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6458 if (GetViewCount()>10000)
6460 // 10k lines is enough to check
6461 TWhitecharsProperties oRet = {true, true, true, true};
6462 return oRet;
6464 TWhitecharsProperties oRet = {};
6465 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6467 if (IsLineEmpty(nViewLine))
6469 continue;
6471 const CString &sLine = GetViewLine(nViewLine);
6472 if (sLine.IsEmpty())
6474 continue;
6476 // check leading whites for convertible tabs and spaces
6477 int nPos = 0;
6478 int nSpaceCount = 0; // number of spaces in tab size run
6479 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6481 switch (sLine[nPos++])
6483 case ' ':
6484 if (++nSpaceCount >= m_nTabSize)
6486 oRet.HasSpacesToConvert = true;
6488 continue;
6489 case '\t':
6490 oRet.HasTabsToConvert = true;
6491 if (nSpaceCount!=0)
6493 oRet.HasSpacesToConvert = true;
6495 continue;
6497 break;
6500 // check trailing whites for removable chars
6501 switch (sLine[sLine.GetLength()-1])
6503 case ' ':
6504 case '\t':
6505 oRet.HasTrailWhiteChars = true;
6508 // check EOLs
6509 EOL eLineEol = GetViewLineEnding(nViewLine);
6510 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6512 oRet.HasMixedEols = true;
6515 return oRet;
6518 void CBaseView::InsertText(const CString& sText)
6520 ResetUndoStep();
6522 POINT ptCaretViewPos = GetCaretViewPosition();
6523 int nLeft = ptCaretViewPos.x;
6524 int nViewLine = ptCaretViewPos.y;
6526 if ((nViewLine == 0) && (GetViewCount() == 0))
6527 OnChar(VK_RETURN, 0, 0);
6529 std::vector<CString> lines;
6530 int nStart = 0;
6531 int nEolPos = 0;
6532 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6534 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6535 lines.push_back(sLine);
6536 nEolPos++;
6537 nStart = nEolPos;
6539 CString sLine = sText.Mid(nStart);
6540 lines.push_back(sLine);
6542 int nLinesToPaste = (int)lines.size();
6543 if (nLinesToPaste > 1)
6545 // multiline text
6547 // We want to undo the multiline insertion in a single step.
6548 CUndo::GetInstance().BeginGrouping();
6550 sLine = GetViewLineChars(nViewLine);
6551 CString sLineLeft = sLine.Left(nLeft);
6552 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6553 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6554 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6555 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6557 newLine.sLine = sLineLeft + lines[0];
6558 SetViewData(nViewLine, newLine);
6561 int nInsertLine = nViewLine;
6562 for (int i = 1; i < nLinesToPaste - 1; i++)
6564 newLine.sLine = lines[i];
6565 InsertViewData(++nInsertLine, newLine);
6567 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6568 newLine.ending = eOriginalEnding;
6569 InsertViewData(++nInsertLine, newLine);
6571 SetModified();
6572 SaveUndoStep();
6574 // adds new lines everywhere except me
6575 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6577 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6579 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6581 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6583 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6585 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6587 SaveUndoStep();
6589 UpdateViewLineNumbers();
6590 CUndo::GetInstance().EndGrouping();
6592 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6594 else
6596 // single line text - just insert it
6597 sLine = GetViewLineChars(nViewLine);
6598 sLine.Insert(nLeft, sText);
6599 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6600 SetViewLine(nViewLine, sLine);
6601 SetViewState(nViewLine, DIFFSTATE_EDITED);
6602 SetModified();
6603 SaveUndoStep();
6606 RefreshViews();
6607 BuildAllScreen2ViewVector();
6608 UpdateCaretViewPosition(ptCaretViewPos);
6611 ULONG CBaseView::GetGestureStatus(CPoint /*ptTouch*/)
6613 return 0;