Update editorconfig
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob3b69bde810e4ae1d9f8239b9c74fc70f1fa38b15
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2016 - TortoiseSVN
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
28 #include "AppUtils.h"
29 #include "GotoLineDlg.h"
30 #include "EncodingDlg.h"
31 #include "EditorConfigWrapper.h"
33 // Note about lines:
34 // We use three different kind of lines here:
35 // 1. The real lines of the original files.
36 // These are shown in the view margin and are not used elsewhere, they're only for user information.
37 // 2. Screen lines.
38 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
39 // 3. View lines.
40 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
42 // Basically view lines are the line data, while screen lines are shown lines.
45 #ifdef _DEBUG
46 #define new DEBUG_NEW
47 #endif
49 #define MARGINWIDTH 20
50 #define HEADERHEIGHT 10
52 #define IDT_SCROLLTIMER 101
54 CBaseView * CBaseView::m_pwndLeft = NULL;
55 CBaseView * CBaseView::m_pwndRight = NULL;
56 CBaseView * CBaseView::m_pwndBottom = NULL;
57 CLocatorBar * CBaseView::m_pwndLocator = NULL;
58 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
59 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
60 CMFCRibbonStatusBar * CBaseView::m_pwndRibbonStatusBar = NULL;
61 CMainFrame * CBaseView::m_pMainFrame = NULL;
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(NULL)
71 , m_pViewData(NULL)
72 , m_pOtherViewData(NULL)
73 , m_pOtherView(NULL)
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(NULL)
101 , m_pFindDialog(NULL)
102 , m_nStatusBarID(0)
103 , m_bMatchCase(false)
104 , m_bLimitToDiff(true)
105 , m_bWholeWord(false)
106 , m_pDC(NULL)
107 , m_pWorkingFile(NULL)
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(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
118 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
119 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
120 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
121 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
122 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
123 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
124 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
125 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?"));
126 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
127 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
128 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
129 m_bEditorConfigEnabled = !!(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\EnableEditorConfig"), FALSE);
130 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
131 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
132 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
133 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
134 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
135 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
136 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
137 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
138 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
139 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
140 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
141 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
142 m_hMarkedIcon = LoadIcon(IDI_LINEMARKED);
143 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
145 for (int i=0; i<1024; ++i)
146 m_sConflictedText += _T("??");
147 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
149 m_szTip[0] = 0;
150 m_wszTip[0] = 0;
151 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
152 EnableToolTips();
154 m_Eols[EOL_LF] = L"\n"; // x0a
155 m_Eols[EOL_CR] = L"\r"; // x0d
156 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
157 m_Eols[EOL_LFCR] = L"\n\r";
158 m_Eols[EOL_VT] = L"\v"; // x0b
159 m_Eols[EOL_FF] = L"\f"; // x0c
160 m_Eols[EOL_NEL] = L"\x85";
161 m_Eols[EOL_LS] = L"\x2028";
162 m_Eols[EOL_PS] = L"\x2029";
163 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
164 ? EOL_CRLF
165 : m_lineendings];
166 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
167 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
170 CBaseView::~CBaseView()
172 ReleaseBitmap();
173 DeleteFonts();
174 DestroyIcon(m_hAddedIcon);
175 DestroyIcon(m_hRemovedIcon);
176 DestroyIcon(m_hConflictedIcon);
177 DestroyIcon(m_hConflictedIgnoredIcon);
178 DestroyIcon(m_hWhitespaceBlockIcon);
179 DestroyIcon(m_hEqualIcon);
180 DestroyIcon(m_hLineEndingCR);
181 DestroyIcon(m_hLineEndingCRLF);
182 DestroyIcon(m_hLineEndingLF);
183 DestroyIcon(m_hEditedIcon);
184 DestroyIcon(m_hMovedIcon);
185 DestroyIcon(m_hMarkedIcon);
186 DestroyCursor(m_margincursor);
189 BEGIN_MESSAGE_MAP(CBaseView, CView)
190 ON_WM_VSCROLL()
191 ON_WM_HSCROLL()
192 ON_WM_ERASEBKGND()
193 ON_WM_CREATE()
194 ON_WM_DESTROY()
195 ON_WM_SIZE()
196 ON_WM_MOUSEWHEEL()
197 ON_WM_MOUSEHWHEEL()
198 ON_WM_SETCURSOR()
199 ON_WM_KILLFOCUS()
200 ON_WM_SETFOCUS()
201 ON_WM_CONTEXTMENU()
202 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
203 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
204 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
205 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
206 ON_WM_KEYDOWN()
207 ON_WM_LBUTTONDOWN()
208 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
209 ON_WM_MOUSEMOVE()
210 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
211 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
212 ON_WM_CHAR()
213 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
214 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
215 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
216 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
217 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
218 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
219 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
220 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
221 ON_WM_TIMER()
222 ON_WM_LBUTTONDBLCLK()
223 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
224 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
225 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
226 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
227 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
228 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
229 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
230 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
231 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
232 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
233 ON_WM_LBUTTONUP()
234 END_MESSAGE_MAP()
237 void CBaseView::DocumentUpdated()
239 ReleaseBitmap();
240 m_nLineHeight = -1;
241 m_nCharWidth = -1;
242 m_nScreenChars = -1;
243 m_nLastScreenChars = -1;
244 m_nMaxLineLength = -1;
245 m_nScreenLines = -1;
246 m_nTopLine = 0;
247 m_bModified = FALSE;
248 m_bOtherDiffChecked = false;
249 m_nDigits = 0;
250 m_nMouseLine = -1;
251 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
252 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
253 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
254 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
255 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
256 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
257 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
258 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
259 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
260 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
261 ? EOL_CRLF
262 : m_lineendings];
263 SetEditorConfigEnabled(m_bEditorConfigEnabled);
264 DeleteFonts();
265 ClearCurrentSelection();
266 UpdateStatusBar();
267 Invalidate();
270 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled)
272 m_bEditorConfigEnabled = bEditorConfigEnabled;
273 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
274 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
275 if (m_bEditorConfigEnabled)
277 m_bEditorConfigLoaded = FALSE; // no editorconfig entries loaded
278 CEditorConfigWrapper ec;
279 if (ec.Load(m_sReflectedName.IsEmpty() ? m_sFullFilePath : m_sReflectedName))
281 m_bEditorConfigLoaded = TRUE;
282 if (ec.m_nTabWidth != nullptr)
283 m_nTabSize = ec.m_nTabWidth;
284 if (ec.m_bIndentStyle != nullptr)
285 m_nTabMode = (m_nTabMode & ~TABMODE_USESPACES) | (ec.m_bIndentStyle ? TABMODE_USESPACES : TABMODE_NONE);
290 static CString GetTabModeString(int nTabMode, int nTabSize, bool bEditorConfig)
292 CString text;
293 if (nTabMode & TABMODE_USESPACES)
294 text = L"Space";
295 else
296 text = L"Tab";
297 text.AppendFormat(L" %d", nTabSize);
298 if (nTabMode & TABMODE_SMARTINDENT)
299 text += L" Smart";
300 if (bEditorConfig)
301 text += L" EC";
302 return text;
305 void CBaseView::UpdateStatusBar()
307 int nRemovedLines = 0;
308 int nAddedLines = 0;
309 int nConflictedLines = 0;
311 if (m_pViewData)
313 for (int i=0; i<m_pViewData->GetCount(); i++)
315 DiffStates state = m_pViewData->GetState(i);
316 switch (state)
318 case DIFFSTATE_ADDED:
319 case DIFFSTATE_IDENTICALADDED:
320 case DIFFSTATE_THEIRSADDED:
321 case DIFFSTATE_YOURSADDED:
322 case DIFFSTATE_CONFLICTADDED:
323 nAddedLines++;
324 break;
325 case DIFFSTATE_IDENTICALREMOVED:
326 case DIFFSTATE_REMOVED:
327 case DIFFSTATE_THEIRSREMOVED:
328 case DIFFSTATE_YOURSREMOVED:
329 nRemovedLines++;
330 break;
331 case DIFFSTATE_CONFLICTED:
332 case DIFFSTATE_CONFLICTED_IGNORED:
333 nConflictedLines++;
334 break;
339 CString sBarText;
340 CString sTemp;
342 if (m_pwndStatusBar)
344 sBarText += CFileTextLines::GetEncodingName(m_texttype);
345 sBarText += sBarText.IsEmpty() ? L"" : L" ";
346 sBarText += GetEolName(m_lineendings);
347 sBarText += sBarText.IsEmpty() ? L"" : L" ";
349 if (sBarText.IsEmpty())
350 sBarText += _T(" / ");
353 if (nRemovedLines)
355 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
356 if (!sBarText.IsEmpty())
357 sBarText += _T(" / ");
358 sBarText += sTemp;
360 if (nAddedLines)
362 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
363 if (!sBarText.IsEmpty())
364 sBarText += _T(" / ");
365 sBarText += sTemp;
367 if (nConflictedLines)
369 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
370 if (!sBarText.IsEmpty())
371 sBarText += _T(" / ");
372 sBarText += sTemp;
374 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
376 if (m_pwndStatusBar)
378 UINT nID;
379 UINT nStyle;
380 int cxWidth;
381 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
383 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
385 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
387 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
388 sBarText = sTemp+sBarText;
390 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
392 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
393 sBarText = sTemp+sBarText;
395 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
396 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
397 //calculate the width of the text
398 CDC * pDC = m_pwndStatusBar->GetDC();
399 if (pDC)
401 CSize size = pDC->GetTextExtent(sBarText);
402 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
403 ReleaseDC(pDC);
405 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
407 else if (m_pwndRibbonStatusBar)
409 if (!IsViewGood(m_pwndBottom))
410 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
411 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
413 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
414 auto apBtnGroupBottom = std::make_unique<CMFCRibbonButtonsGroup>();
415 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
416 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
417 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
418 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
419 apBtnGroupBottom->AddButton(pButton);
420 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
421 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
422 apBtnGroupBottom->AddButton(pButton);
423 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, L"");
424 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMTABMODESTART);
425 apBtnGroupBottom->AddButton(pButton);
426 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
427 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
430 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
431 if (pGroup)
433 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(4));
434 if (pPane)
436 pPane->SetText(sBarText);
438 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
439 if (pButton)
441 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
442 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
444 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
445 if (pButton)
447 pButton->SetText(GetEolName(m_lineendings));
448 pButton->SetDescription(GetEolName(m_lineendings));
450 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(3));
451 if (pButton)
453 pButton->SetText(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
454 pButton->SetDescription(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
457 m_pwndRibbonStatusBar->RecalcLayout();
458 m_pwndRibbonStatusBar->Invalidate();
463 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
465 if (!CView::PreCreateWindow(cs))
466 return FALSE;
468 cs.dwExStyle |= WS_EX_CLIENTEDGE;
469 cs.style &= ~WS_BORDER;
470 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
471 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
473 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
474 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
476 // View must always create its own scrollbars,
477 // if only it's not used within splitter
478 cs.style |= (WS_HSCROLL | WS_VSCROLL);
480 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
481 return TRUE;
484 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
486 int nIndex = 0;
487 if (bBold)
488 nIndex |= 1;
489 if (bItalic)
490 nIndex |= 2;
491 if (m_apFonts[nIndex] == NULL)
493 m_apFonts[nIndex] = new CFont;
494 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
495 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
496 m_lfBaseFont.lfItalic = (BYTE) bItalic;
497 CDC * pDC = GetDC();
498 if (pDC)
500 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
501 ReleaseDC(pDC);
503 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), _countof(m_lfBaseFont.lfFaceName) - 1);
504 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
506 delete m_apFonts[nIndex];
507 m_apFonts[nIndex] = NULL;
508 return CView::GetFont();
511 return m_apFonts[nIndex];
514 void CBaseView::CalcLineCharDim()
516 CDC *pDC = GetDC();
517 if (pDC == nullptr)
518 return;
519 CFont *pOldFont = pDC->SelectObject(GetFont());
520 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
521 pDC->SelectObject(pOldFont);
522 ReleaseDC(pDC);
524 m_nLineHeight = szCharExt.cy;
525 if (m_nLineHeight <= 0)
526 m_nLineHeight = -1;
527 m_nCharWidth = szCharExt.cx;
528 if (m_nCharWidth <= 0)
529 m_nCharWidth = -1;
532 int CBaseView::GetScreenChars()
534 if (m_nScreenChars == -1)
536 CRect rect;
537 GetClientRect(&rect);
538 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
539 if (m_nScreenChars < 0)
540 m_nScreenChars = 0;
542 return m_nScreenChars;
545 int CBaseView::GetAllMinScreenChars() const
547 int nChars = INT_MAX;
548 if (IsLeftViewGood())
549 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
550 if (IsRightViewGood())
551 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
552 if (IsBottomViewGood())
553 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
554 return (nChars==INT_MAX) ? 0 : nChars;
557 int CBaseView::GetAllMaxLineLength() const
559 int nLength = 0;
560 if (IsLeftViewGood())
561 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
562 if (IsRightViewGood())
563 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
564 if (IsBottomViewGood())
565 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
566 return nLength;
569 int CBaseView::GetLineHeight()
571 if (m_nLineHeight == -1)
572 CalcLineCharDim();
573 if (m_nLineHeight <= 0)
574 return 1;
575 return m_nLineHeight;
578 int CBaseView::GetCharWidth()
580 if (m_nCharWidth == -1)
581 CalcLineCharDim();
582 if (m_nCharWidth <= 0)
583 return 1;
584 return m_nCharWidth;
587 int CBaseView::GetMaxLineLength()
589 if (m_nMaxLineLength == -1)
591 m_nMaxLineLength = 0;
592 int nLineCount = GetLineCount();
593 if (nLineCount == 1)
594 return GetLineLengthWithTabsConverted(0);
595 for (int i=0; i<nLineCount; i++)
597 int nActualLength = GetLineLengthWithTabsConverted(i);
598 if (m_nMaxLineLength < nActualLength)
599 m_nMaxLineLength = nActualLength;
602 return m_nMaxLineLength;
605 int CBaseView::GetLineLengthWithTabsConverted(int index)
607 if (m_pViewData == NULL)
608 return 0;
609 if (m_pViewData->GetCount() == 0)
610 return 0;
611 if ((int)m_Screen2View.size() <= index)
612 return 0;
613 CString sLine;
614 if (m_pMainFrame->m_bWrapLines)
615 sLine = GetLineChars(index);
616 else
618 int viewLine = GetViewLineForScreen(index);
619 sLine = m_pViewData->GetLine(viewLine);
621 int tabCount = 0;
622 wchar_t* pChar = (LPWSTR)(LPCWSTR)sLine;
623 auto nLineLength = sLine.GetLength();
624 for (int i = 0; i < nLineLength; ++i)
626 if (*pChar == '\t')
627 ++tabCount;
628 ++pChar;
630 // GetTabSize() - 1 because the tabs are already counted
631 nLineLength = nLineLength + (tabCount * (GetTabSize() - 1));
632 ASSERT(nLineLength >= 0);
633 return nLineLength;
636 int CBaseView::GetLineLength(int index)
638 if (m_pViewData == NULL)
639 return 0;
640 if (m_pViewData->GetCount() == 0)
641 return 0;
642 if ((int)m_Screen2View.size() <= index)
643 return 0;
644 int viewLine = GetViewLineForScreen(index);
645 if (m_pMainFrame->m_bWrapLines)
647 int nLineLength = GetLineChars(index).GetLength();
648 ASSERT(nLineLength >= 0);
649 return nLineLength;
651 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
652 ASSERT(nLineLength >= 0);
653 return nLineLength;
656 int CBaseView::GetViewLineLength(int nViewLine) const
658 if (m_pViewData == NULL)
659 return 0;
660 if (m_pViewData->GetCount() <= nViewLine)
661 return 0;
662 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
663 ASSERT(nLineLength >= 0);
664 return nLineLength;
667 int CBaseView::GetLineCount() const
669 if (m_pViewData == NULL)
670 return 1;
671 int nLineCount = (int)m_Screen2View.size();
672 ASSERT(nLineCount >= 0);
673 return nLineCount;
676 int CBaseView::GetSubLineOffset(int index)
678 return m_Screen2View.GetSubLineOffset(index);
681 CString CBaseView::GetViewLineChars(int nViewLine) const
683 if (m_pViewData == NULL)
684 return 0;
685 if (m_pViewData->GetCount() <= nViewLine)
686 return 0;
687 return m_pViewData->GetLine(nViewLine);
690 CString CBaseView::GetLineChars(int index)
692 if (m_pViewData == NULL)
693 return 0;
694 if (m_pViewData->GetCount() == 0)
695 return 0;
696 if ((int)m_Screen2View.size() <= index)
697 return 0;
698 int viewLine = GetViewLineForScreen(index);
699 if (m_pMainFrame->m_bWrapLines)
701 int subLine = GetSubLineOffset(index);
702 if (subLine >= 0)
704 if (subLine < CountMultiLines(viewLine))
706 return m_ScreenedViewLine[viewLine].SubLines[subLine];
708 return L"";
711 return m_pViewData->GetLine(viewLine);
714 void CBaseView::CheckOtherView()
716 if (m_bOtherDiffChecked)
717 return;
718 // find out what the 'other' file is
719 m_pOtherViewData = NULL;
720 m_pOtherView = NULL;
721 if (this == m_pwndLeft && IsRightViewGood())
723 m_pOtherViewData = m_pwndRight->m_pViewData;
724 m_pOtherView = m_pwndRight;
727 if (this == m_pwndRight && IsLeftViewGood())
729 m_pOtherViewData = m_pwndLeft->m_pViewData;
730 m_pOtherView = m_pwndLeft;
733 m_bOtherDiffChecked = true;
737 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
739 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
740 ASSERT(viewData);
742 DiffStates origstate = viewData->GetState(nLineIndex);
744 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
745 nStartBlock = nLineIndex;
746 nEndBlock = nLineIndex;
747 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
749 DiffStates state = viewData->GetState(nStartBlock - 1);
750 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
751 origstate = state;
752 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
753 nStartBlock--;
754 else
755 break;
757 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
759 DiffStates state = viewData->GetState(nEndBlock + 1);
760 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
761 origstate = state;
762 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
763 nEndBlock++;
764 else
765 break;
769 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
771 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
773 int len = 0;
774 for (int i = nStartBlock; i <= nEndBlock; ++i)
775 len += viewData->GetLine(i).GetLength()+2;
777 CString block;
778 // do not check for whitespace blocks if the line is too long, because
779 // reserving a lot of memory here takes too much time (performance hog)
780 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
781 return block;
782 block.Preallocate(len+1);
783 for (int i = nStartBlock; i <= nEndBlock; ++i)
785 block += viewData->GetLine(i);
786 block += m_Eols[viewData->GetLineEnding(i)];
788 return block;
791 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
793 if (m_pViewData == NULL)
794 return false;
795 bIdentical = false;
796 CheckOtherView();
797 if (!m_pOtherViewData)
798 return false;
799 int viewLine = GetViewLineForScreen(nLineIndex);
800 if (
801 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
802 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
805 bIdentical = true;
806 return false;
808 // first check whether the line itself only has whitespace changes
809 CString mine = m_pViewData->GetLine(viewLine);
810 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
811 if (mine.IsEmpty() && other.IsEmpty())
813 bIdentical = true;
814 return false;
817 if (mine == other)
819 bIdentical = true;
820 return true;
822 FilterWhitespaces(mine, other);
823 if (mine == other)
824 return true;
826 int nStartBlock2, nEndBlock2;
827 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
828 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
829 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
830 if (mine.IsEmpty())
831 bIdentical = false;
832 else
834 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
835 bIdentical = mine == other;
836 FilterWhitespaces(mine, other);
839 return (!mine.IsEmpty()) && (mine == other);
842 bool CBaseView::IsViewLineHidden(int nViewLine)
844 return IsViewLineHidden(m_pViewData, nViewLine);
847 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
849 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
852 int CBaseView::GetLineNumber(int index) const
854 if (m_pViewData == NULL)
855 return -1;
856 int viewLine = GetViewLineForScreen(index);
857 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
858 return -1;
859 return m_pViewData->GetLineNumber(viewLine);
862 int CBaseView::GetScreenLines()
864 if (m_nScreenLines == -1)
866 CRect rect;
867 GetClientRect(&rect);
868 SCROLLBARINFO sbi = { sizeof(sbi) };
869 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
871 // only use the scroll bar size if the info is correct and the scrollbar is visible
872 // if anything isn't proper, assume the scrollbar has a size of zero
873 // and calculate the screen lines without it.
874 if (!(sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) && !(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
876 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
877 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
878 if (m_nScreenLines < 0)
879 m_nScreenLines = 0;
880 return m_nScreenLines;
883 // if the scroll bar is not visible, unavailable or there was an error,
884 // assume the scroll bar height is zero and return the screen lines here.
885 // Of course, that means the cache (using the member variable) won't work
886 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
887 return (rect.Height() - HEADERHEIGHT) / GetLineHeight();
889 return m_nScreenLines;
892 int CBaseView::GetAllMinScreenLines() const
894 int nLines = INT_MAX;
895 if (IsLeftViewGood())
896 nLines = m_pwndLeft->GetScreenLines();
897 if (IsRightViewGood())
898 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
899 if (IsBottomViewGood())
900 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
901 return (nLines == INT_MAX) || (nLines < 0) ? 0 : nLines;
904 int CBaseView::GetAllLineCount() const
906 int nLines = 0;
907 if (IsLeftViewGood())
908 nLines = m_pwndLeft->GetLineCount();
909 if (IsRightViewGood())
910 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
911 if (IsBottomViewGood())
912 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
913 return nLines;
916 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
918 if (IsLeftViewGood())
919 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
920 if (IsRightViewGood())
921 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
922 if (IsBottomViewGood())
923 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
926 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
928 SCROLLINFO si;
929 si.cbSize = sizeof(si);
930 if (bPositionOnly)
932 si.fMask = SIF_POS;
933 si.nPos = m_nTopLine;
935 else
937 EnableScrollBarCtrl(SB_VERT, TRUE);
938 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
940 m_nTopLine = 0;
941 Invalidate();
943 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
944 si.nMin = 0;
945 si.nMax = GetAllLineCount();
946 si.nPage = GetAllMinScreenLines();
947 si.nPos = m_nTopLine;
949 VERIFY(SetScrollInfo(SB_VERT, &si));
952 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
954 CView::OnVScroll(nSBCode, nPos, pScrollBar);
955 if (m_pwndLeft)
956 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
957 if (m_pwndRight)
958 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
959 if (m_pwndBottom)
960 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
961 if (m_pwndLocator)
962 m_pwndLocator->Invalidate();
965 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
967 // Note we cannot use nPos because of its 16-bit nature
968 SCROLLINFO si;
969 si.cbSize = sizeof(si);
970 si.fMask = SIF_ALL;
971 VERIFY(master->GetScrollInfo(SB_VERT, &si));
973 int nPageLines = GetScreenLines();
974 int nLineCount = GetLineCount();
976 int nNewTopLine;
978 static LONG textwidth = 0;
979 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
980 switch (nSBCode)
982 case SB_TOP:
983 nNewTopLine = 0;
984 break;
985 case SB_BOTTOM:
986 nNewTopLine = nLineCount - nPageLines + 1;
987 break;
988 case SB_LINEUP:
989 nNewTopLine = m_nTopLine - 1;
990 break;
991 case SB_LINEDOWN:
992 nNewTopLine = m_nTopLine + 1;
993 break;
994 case SB_PAGEUP:
995 nNewTopLine = m_nTopLine - si.nPage + 1;
996 break;
997 case SB_PAGEDOWN:
998 nNewTopLine = m_nTopLine + si.nPage - 1;
999 break;
1000 case SB_THUMBPOSITION:
1001 m_ScrollTool.Clear();
1002 nNewTopLine = si.nTrackPos;
1003 textwidth = 0;
1004 break;
1005 case SB_THUMBTRACK:
1006 nNewTopLine = si.nTrackPos;
1007 if (GetFocus() == this)
1009 RECT thumbrect;
1010 GetClientRect(&thumbrect);
1011 ClientToScreen(&thumbrect);
1013 POINT thumbpoint;
1014 thumbpoint.x = thumbrect.right;
1015 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
1016 m_ScrollTool.Init(&thumbpoint);
1017 if (textwidth == 0)
1019 CString sTemp = sFormat;
1020 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
1021 textwidth = m_ScrollTool.GetTextWidth(sTemp);
1023 thumbpoint.x -= textwidth;
1024 int line = GetLineNumber(nNewTopLine);
1025 if (line >= 0)
1026 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
1027 else
1028 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
1030 break;
1031 default:
1032 return;
1035 if (nNewTopLine < 0)
1036 nNewTopLine = 0;
1037 if (nNewTopLine >= nLineCount)
1038 nNewTopLine = nLineCount - 1;
1039 ScrollToLine(nNewTopLine);
1042 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
1044 if (IsLeftViewGood())
1045 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
1046 if (IsRightViewGood())
1047 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
1048 if (IsBottomViewGood())
1049 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
1052 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
1054 SCROLLINFO si;
1055 si.cbSize = sizeof(si);
1056 if (bPositionOnly)
1058 si.fMask = SIF_POS;
1059 si.nPos = m_nOffsetChar;
1061 else
1063 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
1064 if (!m_pMainFrame->m_bWrapLines)
1066 int minScreenChars = GetAllMinScreenChars();
1067 int maxLineLength = GetAllMaxLineLength();
1068 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1070 m_nOffsetChar = 0;
1071 Invalidate();
1073 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1074 si.nMin = 0;
1075 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1076 si.nMax += GetMarginWidth()/GetCharWidth();
1077 si.nPage = GetScreenChars();
1078 si.nPos = m_nOffsetChar;
1081 VERIFY(SetScrollInfo(SB_HORZ, &si));
1084 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1086 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1087 if (m_pwndLeft)
1088 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1089 if (m_pwndRight)
1090 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1091 if (m_pwndBottom)
1092 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1093 if (m_pwndLocator)
1094 m_pwndLocator->Invalidate();
1097 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1099 SCROLLINFO si;
1100 si.cbSize = sizeof(si);
1101 si.fMask = SIF_ALL;
1102 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1104 int nPageChars = GetScreenChars();
1105 int nMaxLineLength = GetMaxLineLength();
1107 int nNewOffset;
1108 switch (nSBCode)
1110 case SB_LEFT:
1111 nNewOffset = 0;
1112 break;
1113 case SB_BOTTOM:
1114 nNewOffset = nMaxLineLength - nPageChars + 1;
1115 break;
1116 case SB_LINEUP:
1117 nNewOffset = m_nOffsetChar - 1;
1118 break;
1119 case SB_LINEDOWN:
1120 nNewOffset = m_nOffsetChar + 1;
1121 break;
1122 case SB_PAGEUP:
1123 nNewOffset = m_nOffsetChar - si.nPage + 1;
1124 break;
1125 case SB_PAGEDOWN:
1126 nNewOffset = m_nOffsetChar + si.nPage - 1;
1127 break;
1128 case SB_THUMBPOSITION:
1129 case SB_THUMBTRACK:
1130 nNewOffset = si.nTrackPos;
1131 break;
1132 default:
1133 return;
1136 if (nNewOffset >= nMaxLineLength)
1137 nNewOffset = nMaxLineLength - 1;
1138 if (nNewOffset < 0)
1139 nNewOffset = 0;
1140 ScrollToChar(nNewOffset, TRUE);
1143 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1145 if (m_nOffsetChar != nNewOffsetChar)
1147 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1148 m_nOffsetChar = nNewOffsetChar;
1149 CRect rcScroll;
1150 GetClientRect(&rcScroll);
1151 rcScroll.left += GetMarginWidth();
1152 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1153 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1154 // update the view header
1155 rcScroll.left = 0;
1156 rcScroll.top = 0;
1157 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1158 InvalidateRect(&rcScroll, FALSE);
1159 UpdateWindow();
1160 if (bTrackScrollBar)
1161 RecalcHorzScrollBar(TRUE);
1162 UpdateCaret();
1163 if (m_pwndLineDiffBar)
1164 m_pwndLineDiffBar->Invalidate();
1168 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1170 if (m_pwndLeft)
1171 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1172 if (m_pwndRight)
1173 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1174 if (m_pwndBottom)
1175 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1178 void CBaseView::ScrollAllSide(int delta)
1180 int nNewOffset = m_nOffsetChar;
1181 nNewOffset += delta;
1182 int nMaxLineLength = GetMaxLineLength();
1183 if (nNewOffset >= nMaxLineLength)
1184 nNewOffset = nMaxLineLength - 1;
1185 if (nNewOffset < 0)
1186 nNewOffset = 0;
1187 ScrollAllToChar(nNewOffset, TRUE);
1188 if (m_pwndLineDiffBar)
1189 m_pwndLineDiffBar->Invalidate();
1190 UpdateCaret();
1193 void CBaseView::ScrollSide(int delta)
1195 int nNewOffset = m_nOffsetChar;
1196 nNewOffset += delta;
1197 int nMaxLineLength = GetMaxLineLength();
1198 if (nNewOffset >= nMaxLineLength)
1199 nNewOffset = nMaxLineLength - 1;
1200 if (nNewOffset < 0)
1201 nNewOffset = 0;
1202 ScrollToChar(nNewOffset, TRUE);
1203 if (m_pwndLineDiffBar)
1204 m_pwndLineDiffBar->Invalidate();
1205 UpdateCaret();
1208 void CBaseView::ScrollVertical(short zDelta)
1210 const int nLineCount = GetLineCount();
1211 int nTopLine = m_nTopLine;
1212 nTopLine -= (zDelta/30);
1213 if (nTopLine < 0)
1214 nTopLine = 0;
1215 if (nTopLine >= nLineCount)
1216 nTopLine = nLineCount - 1;
1217 ScrollToLine(nTopLine, TRUE);
1220 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1222 if (m_nTopLine != nNewTopLine)
1224 if (nNewTopLine < 0)
1225 nNewTopLine = 0;
1227 int nScrollLines = m_nTopLine - nNewTopLine;
1229 m_nTopLine = nNewTopLine;
1230 CRect rcScroll;
1231 GetClientRect(&rcScroll);
1232 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1233 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1234 UpdateWindow();
1235 if (bTrackScrollBar)
1236 RecalcVertScrollBar(TRUE);
1237 UpdateCaret();
1242 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1244 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1246 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1248 int nViewLine = GetViewLineForScreen(nLineIndex);
1249 HICON icon = NULL;
1250 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1251 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1252 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1254 DiffStates state = m_pViewData->GetState(nViewLine);
1255 switch (state)
1257 case DIFFSTATE_ADDED:
1258 case DIFFSTATE_THEIRSADDED:
1259 case DIFFSTATE_YOURSADDED:
1260 case DIFFSTATE_IDENTICALADDED:
1261 case DIFFSTATE_CONFLICTADDED:
1262 eIcon = TScreenedViewLine::ICN_ADD;
1263 break;
1264 case DIFFSTATE_REMOVED:
1265 case DIFFSTATE_THEIRSREMOVED:
1266 case DIFFSTATE_YOURSREMOVED:
1267 case DIFFSTATE_IDENTICALREMOVED:
1268 eIcon = TScreenedViewLine::ICN_REMOVED;
1269 break;
1270 case DIFFSTATE_CONFLICTED:
1271 eIcon = TScreenedViewLine::ICN_CONFLICT;
1272 break;
1273 case DIFFSTATE_CONFLICTED_IGNORED:
1274 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1275 break;
1276 case DIFFSTATE_EDITED:
1277 eIcon = TScreenedViewLine::ICN_EDIT;
1278 break;
1279 default:
1280 break;
1282 bool bIdentical = false;
1283 int blockstart = -1;
1284 int blockend = -1;
1285 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1287 if (bIdentical)
1288 eIcon = TScreenedViewLine::ICN_SAME;
1289 else
1290 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1291 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1293 if (nViewLine > blockstart)
1294 Invalidate(); // redraw the upper icons since they're now changing
1295 while (blockstart <= blockend)
1296 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1299 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1300 eIcon = TScreenedViewLine::ICN_MOVED;
1301 if (m_pViewData->GetMarked(nViewLine))
1302 eIcon = TScreenedViewLine::ICN_MARKED;
1303 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1305 switch (eIcon)
1307 case TScreenedViewLine::ICN_UNKNOWN:
1308 case TScreenedViewLine::ICN_NONE:
1309 break;
1310 case TScreenedViewLine::ICN_SAME:
1311 icon = m_hEqualIcon;
1312 break;
1313 case TScreenedViewLine::ICN_EDIT:
1314 icon = m_hEditedIcon;
1315 break;
1316 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1317 icon = m_hWhitespaceBlockIcon;
1318 break;
1319 case TScreenedViewLine::ICN_ADD:
1320 icon = m_hAddedIcon;
1321 break;
1322 case TScreenedViewLine::ICN_CONFLICT:
1323 icon = m_hConflictedIcon;
1324 break;
1325 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1326 icon = m_hConflictedIgnoredIcon;
1327 break;
1328 case TScreenedViewLine::ICN_REMOVED:
1329 icon = m_hRemovedIcon;
1330 break;
1331 case TScreenedViewLine::ICN_MOVED:
1332 icon = m_hMovedIcon;
1333 break;
1334 case TScreenedViewLine::ICN_MARKED:
1335 icon = m_hMarkedIcon;
1336 break;
1340 if (icon)
1342 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1344 if ((m_bViewLinenumbers)&&(m_nDigits))
1346 int nSubLine = GetSubLineOffset(nLineIndex);
1347 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1348 CString sLinenumber;
1349 if (bIsFirstSubline)
1351 CString sLinenumberFormat;
1352 int nLineNumber = GetLineNumber(nLineIndex);
1353 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1355 // TODO: do not show if there is no number hidden
1356 // TODO: show number if there is only one
1357 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1358 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1360 else if (nLineNumber >= 0)
1362 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1363 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1365 else if (m_pMainFrame->m_bWrapLines)
1367 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1368 sLinenumber.Format(sLinenumberFormat, _T("·"));
1370 if (!sLinenumber.IsEmpty())
1372 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1373 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1375 pdc->SelectObject(GetFont());
1376 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1383 int CBaseView::GetMarginWidth()
1385 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1387 if (m_nDigits <= 0)
1389 int nLength = (int)m_pViewData->GetCount();
1390 // find out how many digits are needed to show the highest line number
1391 CString sMax;
1392 sMax.Format(_T("%d"), nLength);
1393 m_nDigits = sMax.GetLength();
1395 int nWidth = GetCharWidth();
1396 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1398 return MARGINWIDTH;
1401 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1403 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1404 COLORREF crBk, crFg;
1405 if (IsBottomViewGood())
1407 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1408 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1410 else
1412 DiffStates state = DIFFSTATE_REMOVED;
1413 if (this == m_pwndRight)
1415 state = DIFFSTATE_ADDED;
1417 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1419 pdc->SetBkColor(crBk);
1420 pdc->FillSolidRect(textrect, crBk);
1422 pdc->SetTextColor(crFg);
1424 pdc->SelectObject(GetFont(FALSE, TRUE));
1426 CString sViewTitle;
1427 if (IsModified())
1429 sViewTitle = _T("* ") + m_sWindowName;
1431 else
1433 sViewTitle = m_sWindowName;
1435 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1436 if (nStringLength > rect.Width())
1438 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1439 sViewTitle = m_sWindowName.Mid(offset);
1441 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1442 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1443 if (this->GetFocus() == this)
1444 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1445 else
1446 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1449 void CBaseView::OnDraw(CDC * pDC)
1451 CRect rcClient;
1452 GetClientRect(rcClient);
1454 int nLineCount = GetLineCount();
1455 int nLineHeight = GetLineHeight();
1457 CDC cacheDC;
1458 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1459 if (m_pCacheBitmap == NULL)
1461 m_pCacheBitmap = new CBitmap;
1462 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1464 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1466 DrawHeader(pDC, rcClient);
1468 CRect rcLine;
1469 rcLine = rcClient;
1470 rcLine.top += nLineHeight+HEADERHEIGHT;
1471 rcLine.bottom = rcLine.top + nLineHeight;
1472 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1473 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1475 int nCurrentLine = m_nTopLine;
1476 bool bBeyondFileLineCached = false;
1477 while (rcLine.top < rcClient.bottom)
1479 if (nCurrentLine < nLineCount)
1481 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1482 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1483 bBeyondFileLineCached = false;
1485 else if (!bBeyondFileLineCached)
1487 DrawMargin(&cacheDC, rcCacheMargin, -1);
1488 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1489 bBeyondFileLineCached = true;
1492 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1494 nCurrentLine ++;
1495 rcLine.OffsetRect(0, nLineHeight);
1498 cacheDC.SelectObject(pOldBitmap);
1499 cacheDC.DeleteDC();
1502 bool CBaseView::IsStateConflicted(DiffStates state)
1504 switch (state)
1506 case DIFFSTATE_CONFLICTED:
1507 case DIFFSTATE_CONFLICTED_IGNORED:
1508 case DIFFSTATE_CONFLICTEMPTY:
1509 case DIFFSTATE_CONFLICTADDED:
1510 return true;
1512 return false;
1515 bool CBaseView::IsStateEmpty(DiffStates state)
1517 switch (state)
1519 case DIFFSTATE_CONFLICTEMPTY:
1520 case DIFFSTATE_UNKNOWN:
1521 case DIFFSTATE_EMPTY:
1522 return true;
1524 return false;
1527 bool CBaseView::IsStateRemoved(DiffStates state)
1529 switch (state)
1531 case DIFFSTATE_REMOVED:
1532 case DIFFSTATE_THEIRSREMOVED:
1533 case DIFFSTATE_YOURSREMOVED:
1534 case DIFFSTATE_IDENTICALREMOVED:
1535 return true;
1537 return false;
1540 DiffStates CBaseView::ResolveState(DiffStates state)
1542 if (IsStateConflicted(state))
1544 if (state == DIFFSTATE_CONFLICTEMPTY)
1545 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1546 else
1547 return DIFFSTATE_CONFLICTRESOLVED;
1549 return state;
1553 bool CBaseView::IsLineEmpty(int nLineIndex)
1555 if (m_pViewData == 0)
1556 return FALSE;
1557 int nViewLine = GetViewLineForScreen(nLineIndex);
1558 return IsViewLineEmpty(nViewLine);
1561 bool CBaseView::IsViewLineEmpty(int nViewLine)
1563 if (m_pViewData == 0)
1564 return FALSE;
1565 const DiffStates state = m_pViewData->GetState(nViewLine);
1566 return IsStateEmpty(state);
1569 bool CBaseView::IsLineRemoved(int nLineIndex)
1571 if (m_pViewData == 0)
1572 return FALSE;
1573 int nViewLine = GetViewLineForScreen(nLineIndex);
1574 return IsViewLineRemoved(nViewLine);
1577 bool CBaseView::IsViewLineRemoved(int nViewLine)
1579 if (m_pViewData == 0)
1580 return FALSE;
1581 const DiffStates state = m_pViewData->GetState(nViewLine);
1582 return IsStateRemoved(state);
1585 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1587 if (m_pViewData == 0)
1588 return false;
1589 const DiffStates state = m_pViewData->GetState(nLineIndex);
1590 return IsStateConflicted(state);
1593 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1595 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1598 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1600 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1603 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1605 if (origin.x < (rc.left - GetCharWidth() +1))
1606 return;
1607 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1608 return;
1609 int viewLine = GetViewLineForScreen(nLineIndex);
1610 EOL ending = m_pViewData->GetLineEnding(viewLine);
1611 if (m_bIconLFs)
1613 HICON hEndingIcon = NULL;
1614 switch (ending)
1616 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1617 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1618 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1619 default: return;
1621 // If EOL style has changed, color end-of-line markers as inline differences.
1623 m_bShowInlineDiff && m_pOtherViewData &&
1624 (viewLine < m_pOtherViewData->GetCount()) &&
1625 (ending != EOL_NOENDING) &&
1626 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1627 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1630 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1633 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1635 else
1637 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1638 CPen * oldpen = pDC->SelectObject(&pen);
1639 int yMiddle = origin.y + rc.Height()/2;
1640 int xMiddle = origin.x+GetCharWidth()/2;
1641 bool bMultiline = false;
1642 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1644 if (GetLineLength(nLineIndex+1))
1646 // multiline
1647 bMultiline = true;
1648 pDC->MoveTo(origin.x, yMiddle-2);
1649 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1650 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1651 pDC->LineTo(origin.x, yMiddle+2);
1653 else if (GetLineLength(nLineIndex) == 0)
1654 bMultiline = true;
1656 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1657 bMultiline = true;
1659 if (!bMultiline)
1661 switch (ending)
1663 case EOL_AUTOLINE:
1664 case EOL_CRLF:
1665 // arrow from top to middle+2, then left
1666 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1667 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1668 case EOL_CR:
1669 // arrow from right to left
1670 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1671 pDC->LineTo(origin.x, yMiddle);
1672 pDC->LineTo(origin.x+4, yMiddle+4);
1673 pDC->MoveTo(origin.x, yMiddle);
1674 pDC->LineTo(origin.x+4, yMiddle-4);
1675 break;
1676 case EOL_LFCR:
1677 // from right-upper to left then down
1678 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1679 pDC->LineTo(xMiddle, yMiddle-2);
1680 pDC->LineTo(xMiddle, rc.bottom-1);
1681 pDC->LineTo(xMiddle+4, rc.bottom-5);
1682 pDC->MoveTo(xMiddle, rc.bottom-1);
1683 pDC->LineTo(xMiddle-4, rc.bottom-5);
1684 break;
1685 case EOL_LF:
1686 // arrow from top to bottom
1687 pDC->MoveTo(xMiddle, rc.top);
1688 pDC->LineTo(xMiddle, rc.bottom-1);
1689 pDC->LineTo(xMiddle+4, rc.bottom-5);
1690 pDC->MoveTo(xMiddle, rc.bottom-1);
1691 pDC->LineTo(xMiddle-4, rc.bottom-5);
1692 break;
1693 case EOL_FF: // Form Feed, U+000C
1694 case EOL_NEL: // Next Line, U+0085
1695 case EOL_LS: // Line Separator, U+2028
1696 case EOL_PS: // Paragraph Separator, U+2029
1697 // draw a horizontal line at the bottom of this line
1698 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1699 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1700 pDC->LineTo(origin.x, rc.bottom-2);
1701 pDC->LineTo(origin.x+5, rc.bottom-2);
1702 pDC->MoveTo(origin.x, rc.bottom-2);
1703 pDC->LineTo(origin.x+1, rc.bottom-6);
1704 break;
1705 default: // other EOLs
1706 // arrow from top right to bottom left
1707 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1708 pDC->LineTo(origin.x, rc.bottom-1);
1709 pDC->LineTo(origin.x+5, rc.bottom-2);
1710 pDC->MoveTo(origin.x, rc.bottom-1);
1711 pDC->LineTo(origin.x+1, rc.bottom-6);
1712 break;
1713 case EOL_NOENDING:
1714 break;
1717 pDC->SelectObject(oldpen);
1721 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1723 if (!m_bShowSelection)
1724 return;
1726 int nSelBlockStart;
1727 int nSelBlockEnd;
1728 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1729 return;
1731 const int THICKNESS = 2;
1732 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1734 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1735 int nSubLine = GetSubLineOffset(nLineIndex);
1736 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1737 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1739 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1742 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1743 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1745 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1749 void CBaseView::DrawTextLine(
1750 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1752 ASSERT(nLineIndex < GetLineCount());
1753 int nViewLine = GetViewLineForScreen(nLineIndex);
1754 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1756 LineColors lineCols = GetLineColors(nViewLine);
1758 CString sViewLine = GetViewLineChars(nViewLine);
1759 // mark selection
1760 if (m_bShowSelection && HasTextSelection())
1762 // has this line selection ?
1763 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1765 int nViewLineLength = sViewLine.GetLength();
1767 // first suppose the whole line is selected
1768 int selectedStart = 0;
1769 int selectedEnd = nViewLineLength;
1771 // the view line is partially selected
1772 if (m_ptSelectionViewPosStart.y == nViewLine)
1774 selectedStart = m_ptSelectionViewPosStart.x;
1777 if (m_ptSelectionViewPosEnd.y == nViewLine)
1779 selectedEnd = m_ptSelectionViewPosEnd.x;
1781 // apply selection coloring
1782 // First enforce start and end point
1783 lineCols.SplitBlock(selectedStart);
1784 lineCols.SplitBlock(selectedEnd);
1785 // change color of affected parts
1786 long intenseColorScale = m_bFocused ? 70 : 30;
1787 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1788 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1790 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1791 if (it->second.shot == it->second.background)
1793 it->second.shot = crBk;
1795 it->second.background = crBk;
1796 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1801 // TODO: remove duplicate from selection and mark
1802 if (!m_sMarkedWord.IsEmpty())
1804 int nMarkLength = m_sMarkedWord.GetLength();
1805 //int nViewLineLength = sViewLine.GetLength();
1806 const TCHAR * text = sViewLine;
1807 const TCHAR * findText = text;
1808 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1810 int nMarkStart = static_cast<int>(findText - text);
1811 int nMarkEnd = nMarkStart + nMarkLength;
1812 findText += nMarkLength;
1813 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1814 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1815 if (eLeft == eStart)
1816 continue;
1817 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1818 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1819 if (eRight == eEnd)
1820 continue;
1822 // First enforce start and end point
1823 lineCols.SplitBlock(nMarkStart);
1824 lineCols.SplitBlock(nMarkEnd);
1825 // change color of affected parts
1826 const long int nIntenseColorScale = 200;
1827 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1828 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1830 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1831 if (it->second.shot == it->second.background)
1833 it->second.shot = crBk;
1835 it->second.background = crBk;
1836 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1840 if (!m_sFindText.IsEmpty())
1842 int nMarkStart = 0;
1843 int nMarkEnd = 0;
1844 int nStringPos = nMarkStart;
1845 CString searchLine = sViewLine;
1846 if (!m_bMatchCase)
1847 searchLine.MakeLower();
1848 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1850 // First enforce start and end point
1851 lineCols.SplitBlock(nMarkStart+nStringPos);
1852 lineCols.SplitBlock(nMarkEnd+nStringPos);
1853 // change color of affected parts
1854 const long int nIntenseColorScale = 30;
1855 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1856 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1858 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1859 if (it->second.shot == it->second.background)
1861 it->second.shot = crBk;
1863 it->second.background = crBk;
1864 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1866 searchLine = searchLine.Mid(nMarkEnd);
1867 nStringPos = nMarkEnd;
1868 nMarkStart = 0;
1869 nMarkEnd = 0;
1873 // @ this point we may cache data for next line which may be same in wrapped mode
1875 int nTextOffset = 0;
1876 int nSubline = GetSubLineOffset(nLineIndex);
1877 for (int n=0; n<nSubline; n++)
1879 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1880 nTextOffset += sLine.GetLength();
1883 CString sLine = GetLineChars(nLineIndex);
1884 int nLineLength = sLine.GetLength();
1885 CString sLineExp = ExpandChars(sLine);
1886 LPCTSTR textExp = sLineExp;
1887 //int nLineLengthExp = sLineExp.GetLength();
1888 int nStartExp = 0;
1889 int nLeft = coords.x;
1890 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1892 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1893 ++itEnd;
1894 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1895 int nEnd = nLineLength;
1896 if (itEnd != lineCols.end())
1898 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1900 int nBlockLength = nEnd - nStart;
1901 if (nBlockLength > 0 && nEnd>=0)
1903 pDC->SetBkColor(itStart->second.background);
1904 pDC->SetTextColor(itStart->second.text);
1905 int nEndExp = CountExpandedChars(sLine, nEnd);
1906 int nTextLength = nEndExp - nStartExp;
1907 LPCTSTR p_zBlockText = textExp + nStartExp;
1908 SIZE Size;
1909 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1910 int nRight = nLeft + Size.cx;
1911 if ((nRight > rc.left) && (nLeft < rc.right))
1913 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1914 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1915 // is 4094 (4095 doesn't work anymore).
1916 // So we limit the length here to that 4094 chars.
1917 // In case we're scrolled to the right, there's no need to draw the string
1918 // from way outside our window, so we also offset the drawing to the start of the window.
1919 // This reduces the string length as well.
1920 int offset = 0;
1921 int leftcoord = nLeft;
1922 if (nLeft < 0)
1924 int fit = nTextLength;
1925 auto posBuffer = std::make_unique<int[]>(fit);
1926 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1927 int lower = 0, upper = fit - 1;
1930 int middle = (upper + lower + 1) / 2;
1931 int width = posBuffer.get()[middle];
1932 if (rc.left - nLeft < width)
1933 upper = middle - 1;
1934 else
1935 lower = middle;
1936 } while (lower < upper);
1938 offset = lower;
1939 nTextLength -= offset;
1940 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1943 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1944 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1946 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1949 nLeft = nRight;
1950 coords.x = nRight;
1951 nStartExp = nEndExp;
1956 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1958 if (nLineIndex >= GetLineCount())
1959 nLineIndex = -1;
1960 ASSERT(nLineIndex >= -1);
1962 if ((nLineIndex == -1) || !m_pViewData)
1964 // Draw line beyond the text
1965 COLORREF crBkgnd, crText;
1966 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1967 pDC->FillSolidRect(rc, crBkgnd);
1968 return;
1971 int viewLine = GetViewLineForScreen(nLineIndex);
1972 if (m_pMainFrame->m_bCollapsed)
1974 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1976 COLORREF crBkgnd, crText;
1977 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1978 pDC->FillSolidRect(rc, crBkgnd);
1980 const int THICKNESS = 2;
1981 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1982 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1983 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1984 pDC->SetBkColor(crBkgnd);
1985 CRect rect = rc;
1986 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1987 return;
1991 DiffStates diffState = m_pViewData->GetState(viewLine);
1992 COLORREF crBkgnd, crText;
1993 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1995 if (diffState == DIFFSTATE_CONFLICTED)
1997 // conflicted lines are shown without 'text' on them
1998 CRect rect = rc;
1999 pDC->FillSolidRect(rc, crBkgnd);
2000 // now draw some faint text patterns
2001 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
2002 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
2003 DrawBlockLine(pDC, rc, nLineIndex);
2004 return;
2007 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
2008 CString sLine = GetLineChars(nLineIndex);
2009 if (sLine.IsEmpty())
2011 pDC->FillSolidRect(rc, crBkgnd);
2012 DrawBlockLine(pDC, rc, nLineIndex);
2013 DrawLineEnding(pDC, rc, nLineIndex, origin);
2014 return;
2017 CheckOtherView();
2019 // Draw the line
2021 pDC->SelectObject(GetFont(FALSE, FALSE));
2023 DrawTextLine(pDC, rc, nLineIndex, origin);
2025 // draw white space after the end of line
2026 CRect frect = rc;
2027 if (origin.x > frect.left)
2028 frect.left = origin.x;
2029 if (frect.right > frect.left)
2030 pDC->FillSolidRect(frect, crBkgnd);
2032 // draw the whitespace chars
2033 LPCTSTR pszChars = (LPCWSTR)sLine;
2034 if (m_bViewWhitespace)
2036 int xpos = 0;
2037 int nChars = 0;
2038 LPCTSTR pLastSpace = pszChars;
2039 int y = rc.top + (rc.bottom-rc.top)/2;
2040 xpos -= m_nOffsetChar * GetCharWidth();
2042 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2043 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
2044 while (*pszChars)
2046 switch (*pszChars)
2048 case '\t':
2050 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2051 pLastSpace = pszChars + 1;
2052 // draw an arrow
2053 int nSpaces = GetTabSize() - nChars % GetTabSize();
2054 if (xpos + nSpaces * GetCharWidth() > 0)
2056 int xposreal = max(xpos, 0);
2057 if ((xposreal > 0) || (nSpaces > 0))
2059 CPen * oldPen = pDC->SelectObject(&pen);
2060 pDC->MoveTo(xposreal + rc.left, y);
2061 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2062 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y - 4);
2063 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2064 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y + 4);
2065 pDC->SelectObject(oldPen);
2068 xpos += nSpaces * GetCharWidth();
2069 nChars += nSpaces;
2071 break;
2072 case ' ':
2074 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2075 pLastSpace = pszChars + 1;
2076 // draw a small dot
2077 if (xpos >= 0)
2079 CPen * oldPen = pDC->SelectObject(&pen2);
2080 pDC->MoveTo(xpos + rc.left + GetCharWidth()/2-1, y);
2081 pDC->LineTo(xpos + rc.left + GetCharWidth()/2+1, y);
2082 pDC->SelectObject(oldPen);
2084 xpos += GetCharWidth();
2085 nChars++;
2087 break;
2088 default:
2089 nChars++;
2090 break;
2092 pszChars++;
2095 DrawBlockLine(pDC, rc, nLineIndex);
2096 if (origin.x >= rc.left)
2097 DrawLineEnding(pDC, rc, nLineIndex, origin);
2100 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2102 if (nCount <= 0)
2104 line.Empty();
2105 return;
2108 int nTabSize = GetTabSize();
2110 int nActualOffset = CountExpandedChars(sLine, nOffset);
2112 LPCTSTR pszChars = (LPCWSTR)sLine;
2113 pszChars += nOffset;
2114 int nLength = nCount;
2116 int nTabCount = 0;
2117 for (int i=0; i<nLength; i++)
2119 if (pszChars[i] == _T('\t'))
2120 nTabCount ++;
2123 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2124 int nCurPos = 0;
2125 if (nTabCount > 0 || m_bViewWhitespace)
2127 for (int i=0; i<nLength; i++)
2129 if (pszChars[i] == _T('\t'))
2131 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2132 while (nSpaces > 0)
2134 pszBuf[nCurPos ++] = _T(' ');
2135 nSpaces --;
2138 else
2140 pszBuf[nCurPos] = pszChars[i];
2141 nCurPos ++;
2145 else
2147 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2148 nCurPos = nLength;
2150 pszBuf[nCurPos] = 0;
2151 line.ReleaseBuffer();
2154 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2156 CString sRet;
2157 int nLength = sLine.GetLength();
2158 ExpandChars(sLine, nOffset, nLength, sRet);
2159 return sRet;
2162 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2164 int nTabSize = GetTabSize();
2166 int nActualOffset = 0;
2167 for (int i=0; i<nLength; i++)
2169 if (sLine[i] == _T('\t'))
2170 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2171 else
2172 nActualOffset ++;
2174 return nActualOffset;
2177 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2179 if (m_pwndLeft)
2180 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2181 if (m_pwndRight)
2182 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2183 if (m_pwndBottom)
2184 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2185 if (m_pwndLocator)
2186 m_pwndLocator->Invalidate();
2189 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2191 //almost the same as ScrollAllToLine, but try to put the line in the
2192 //middle of the view, not on top
2193 int nNewTopLine = nNewLine - GetScreenLines()/2;
2194 if (nNewTopLine < 0)
2195 nNewTopLine = 0;
2196 if (nNewTopLine >= (int)m_Screen2View.size())
2197 nNewTopLine = (int)m_Screen2View.size()-1;
2198 if (bAll)
2199 ScrollAllToLine(nNewTopLine);
2200 else
2201 ScrollToLine(nNewTopLine);
2204 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2206 return TRUE;
2209 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2211 if (CView::OnCreate(lpCreateStruct) == -1)
2212 return -1;
2214 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2215 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2216 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2217 m_lfBaseFont.lfHeight = 0;
2218 m_lfBaseFont.lfWeight = FW_NORMAL;
2219 m_lfBaseFont.lfItalic = FALSE;
2220 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2221 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2222 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2223 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2224 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2226 return 0;
2229 void CBaseView::OnDestroy()
2231 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2233 m_pFindDialog->SendMessage(WM_CLOSE);
2234 return;
2236 CView::OnDestroy();
2237 DeleteFonts();
2238 ReleaseBitmap();
2241 void CBaseView::OnSize(UINT nType, int cx, int cy)
2243 CView::OnSize(nType, cx, cy);
2244 ReleaseBitmap();
2246 m_nScreenLines = -1;
2247 m_nScreenChars = -1;
2248 if (m_nLastScreenChars != GetScreenChars())
2250 BuildAllScreen2ViewVector();
2251 m_nLastScreenChars = m_nScreenChars;
2252 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2254 // if we're in wrap mode, the line wrapping most likely changed
2255 // and that means we have to redraw the whole window, not just the
2256 // scrolled part.
2257 Invalidate(FALSE);
2259 else
2261 // make sure the view header is redrawn
2262 CRect rcScroll;
2263 GetClientRect(&rcScroll);
2264 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2265 InvalidateRect(&rcScroll, FALSE);
2268 else
2270 // make sure the view header is redrawn
2271 CRect rcScroll;
2272 GetClientRect(&rcScroll);
2273 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2274 InvalidateRect(&rcScroll, FALSE);
2276 UpdateLocator();
2277 RecalcVertScrollBar();
2278 RecalcHorzScrollBar();
2280 UpdateCaret();
2283 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2285 if (m_pwndLeft)
2286 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2287 if (m_pwndRight)
2288 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2289 if (m_pwndBottom)
2290 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2291 if (m_pwndLocator)
2292 m_pwndLocator->Invalidate();
2293 return CView::OnMouseWheel(nFlags, zDelta, pt);
2296 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2298 if (m_pwndLeft)
2299 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2300 if (m_pwndRight)
2301 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2302 if (m_pwndBottom)
2303 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2304 if (m_pwndLocator)
2305 m_pwndLocator->Invalidate();
2308 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2310 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2311 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2313 if (bControl || bShift)
2315 if (m_pMainFrame->m_bWrapLines)
2316 return;
2317 // Ctrl-Wheel scrolls sideways
2318 ScrollSide(-zDelta/30);
2320 else
2322 ScrollVertical(zDelta);
2326 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2328 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2329 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2331 if (bControl || bShift)
2333 ScrollVertical(zDelta);
2335 else
2337 if (m_pMainFrame->m_bWrapLines)
2338 return;
2339 // Ctrl-Wheel scrolls sideways
2340 ScrollSide(-zDelta/30);
2344 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2346 if (nHitTest == HTCLIENT)
2348 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2350 if (m_nMouseLine < (int)m_Screen2View.size())
2352 if (m_nMouseLine >= 0)
2354 int viewLine = GetViewLineForScreen(m_nMouseLine);
2355 if (viewLine < m_pViewData->GetCount())
2357 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2359 ::SetCursor(::LoadCursor(NULL, IDC_HAND));
2360 return TRUE;
2366 if (m_mouseInMargin)
2368 ::SetCursor(m_margincursor);
2369 return TRUE;
2371 if (m_nMouseLine >= 0)
2373 ::SetCursor(::LoadCursor(NULL, IDC_IBEAM)); // Set To Edit Cursor
2374 return TRUE;
2377 ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); // Set To Arrow Cursor
2378 return TRUE;
2380 return CView::OnSetCursor(pWnd, nHitTest, message);
2383 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2385 CView::OnKillFocus(pNewWnd);
2386 m_bFocused = FALSE;
2387 UpdateCaret();
2388 Invalidate();
2391 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2393 CView::OnSetFocus(pOldWnd);
2394 m_bFocused = TRUE;
2395 UpdateCaret();
2396 Invalidate();
2399 int CBaseView::GetLineFromPoint(CPoint point)
2401 ScreenToClient(&point);
2402 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2405 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2407 if (!this->IsWindowVisible())
2408 return;
2410 CIconMenu popup;
2411 if (!popup.CreatePopupMenu())
2412 return;
2414 AddContextItems(popup, state);
2416 CMenu popupEols;
2417 CMenu popupUnicode;
2418 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2419 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2420 if (IsWritable())
2422 CString temp;
2423 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2424 temp.LoadString(IDS_EDIT_TAB2SPACE);
2425 popup.AppendMenu(MF_STRING | (oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_TABTOSPACES, temp);
2426 temp.LoadString(IDS_EDIT_SPACE2TAB);
2427 popup.AppendMenu(MF_STRING | (oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_SPACESTOTABS, temp);
2428 temp.LoadString(IDS_EDIT_TRIM);
2429 popup.AppendMenu(MF_STRING | (oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2431 // add eol submenu
2432 if (!popupEols.CreatePopupMenu())
2433 return;
2435 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2436 for (int i = 1; i < _countof(eolArray); i++)
2438 temp = GetEolName(eolArray[i]);
2439 bool bChecked = (eEolType == eolArray[i]);
2440 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2443 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2444 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2446 // add encoding submenu
2447 if (!popupUnicode.CreatePopupMenu())
2448 return;
2449 for (int i = 0; i < _countof(uctArray); i++)
2451 temp = CFileTextLines::GetEncodingName(uctArray[i]);
2452 bool bChecked = (m_texttype == uctArray[i]);
2453 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2455 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2456 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2460 CompensateForKeyboard(point);
2462 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2463 ResetUndoStep();
2464 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2466 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2468 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2470 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2471 SaveUndoStep();
2473 switch (cmd)
2475 // 2-pane view commands; target is right view
2476 case POPUPCOMMAND_USELEFTBLOCK:
2477 m_pwndRight->UseLeftBlock();
2478 break;
2479 case POPUPCOMMAND_USELEFTFILE:
2480 m_pwndRight->UseLeftFile();
2481 break;
2482 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2483 m_pwndRight->UseBothLeftFirst();
2484 break;
2485 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2486 m_pwndRight->UseBothRightFirst();
2487 break;
2488 case POPUPCOMMAND_MARKBLOCK:
2489 m_pwndRight->MarkBlock(true);
2490 break;
2491 case POPUPCOMMAND_UNMARKBLOCK:
2492 m_pwndRight->MarkBlock(false);
2493 break;
2494 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2495 m_pwndRight->LeaveOnlyMarkedBlocks();
2496 break;
2497 // 2-pane view multiedit commands; target is left view
2498 case POPUPCOMMAND_PREPENDFROMRIGHT:
2499 if (!m_pwndLeft->IsReadonly())
2500 m_pwndLeft->UseBothRightFirst();
2501 break;
2502 case POPUPCOMMAND_REPLACEBYRIGHT:
2503 if (!m_pwndLeft->IsReadonly())
2504 m_pwndLeft->UseRightBlock();
2505 break;
2506 case POPUPCOMMAND_APPENDFROMRIGHT:
2507 if (!m_pwndLeft->IsReadonly())
2508 m_pwndLeft->UseBothLeftFirst();
2509 break;
2510 case POPUPCOMMAND_USERIGHTFILE:
2511 m_pwndLeft->UseRightFile();
2512 break;
2513 // 3-pane view commands; target is bottom view
2514 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2515 m_pwndBottom->UseBothRightFirst();
2516 break;
2517 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2518 m_pwndBottom->UseBothLeftFirst();
2519 break;
2520 case POPUPCOMMAND_USEYOURBLOCK:
2521 m_pwndBottom->UseRightBlock();
2522 break;
2523 case POPUPCOMMAND_USEYOURFILE:
2524 m_pwndBottom->UseRightFile();
2525 break;
2526 case POPUPCOMMAND_USETHEIRBLOCK:
2527 m_pwndBottom->UseLeftBlock();
2528 break;
2529 case POPUPCOMMAND_USETHEIRFILE:
2530 m_pwndBottom->UseLeftFile();
2531 break;
2532 // copy, cut and paste commands
2533 case ID_EDIT_COPY:
2534 OnEditCopy();
2535 break;
2536 case ID_EDIT_CUT:
2537 OnEditCut();
2538 break;
2539 case ID_EDIT_PASTE:
2540 OnEditPaste();
2541 break;
2542 // white chars manipulations
2543 case POPUPCOMMAND_TABTOSPACES:
2544 ConvertTabToSpaces();
2545 break;
2546 case POPUPCOMMAND_SPACESTOTABS:
2547 Tabularize();
2548 break;
2549 case POPUPCOMMAND_REMOVETRAILWHITES:
2550 RemoveTrailWhiteChars();
2551 break;
2552 default:
2553 return;
2554 } // switch (cmd)
2555 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2556 return;
2559 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2561 if (!m_pViewData)
2562 return;
2564 int nViewBlockStart = -1;
2565 int nViewBlockEnd = -1;
2566 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2567 if ((point.x >= 0) && (point.y >= 0))
2569 int nLine = GetLineFromPoint(point)-1;
2570 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2572 int nViewLine = GetViewLineForScreen(nLine);
2573 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2575 ClearSelection(); // Clear text-copy selection
2577 nViewBlockStart = nViewLine;
2578 nViewBlockEnd = nViewLine;
2579 DiffStates state = m_pViewData->GetState(nViewLine);
2580 while (nViewBlockStart > 0)
2582 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2583 if (!LinesInOneChange(-1, state, lineState))
2584 break;
2585 nViewBlockStart--;
2588 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2590 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2591 if (!LinesInOneChange(1, state, lineState))
2592 break;
2593 nViewBlockEnd++;
2596 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2597 UpdateCaretPosition(point);
2602 // FixSelection(); fix selection range
2603 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2604 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2606 DiffStates state = DIFFSTATE_UNKNOWN;
2607 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2609 // find a more 'relevant' state in the selection
2610 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2612 state = m_pViewData->GetState(i);
2613 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2614 break;
2617 OnContextMenu(point, state);
2620 void CBaseView::RefreshViews()
2622 if (m_pwndLeft)
2624 m_pwndLeft->UpdateStatusBar();
2625 m_pwndLeft->Invalidate();
2627 if (m_pwndRight)
2629 m_pwndRight->UpdateStatusBar();
2630 m_pwndRight->Invalidate();
2632 if (m_pwndBottom)
2634 m_pwndBottom->UpdateStatusBar();
2635 m_pwndBottom->Invalidate();
2637 if (m_pwndLocator)
2638 m_pwndLocator->Invalidate();
2641 void CBaseView::GoToFirstDifference()
2643 SetCaretToFirstViewLine();
2644 SelectNextBlock(1, false, false);
2647 void CBaseView::GoToFirstConflict()
2649 SetCaretToFirstViewLine();
2650 SelectNextBlock(1, true, false);
2653 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2655 ClearSelection();
2656 SetupAllSelection(nStart, max(nStart, nEnd));
2658 UpdateCaretPosition(SetupPoint(0, nStart));
2659 Invalidate();
2662 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2664 ClearSelection();
2665 SetupAllViewSelection(nStart, max(nStart, nEnd));
2667 UpdateCaretViewPosition(SetupPoint(0, nStart));
2668 Invalidate();
2671 void CBaseView::SetupAllViewSelection(int start, int end)
2673 SetupViewSelection(m_pwndBottom, start, end);
2674 SetupViewSelection(m_pwndLeft, start, end);
2675 SetupViewSelection(m_pwndRight, start, end);
2678 void CBaseView::SetupAllSelection(int start, int end)
2680 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2683 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2685 void CBaseView::SetupSelection(int start, int end)
2687 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2690 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2692 if (!IsViewGood(view))
2693 return;
2694 view->SetupViewSelection(start, end);
2697 void CBaseView::SetupViewSelection(int start, int end)
2699 // clear text selection before setting line selection ?
2700 m_nSelViewBlockStart = start;
2701 m_nSelViewBlockEnd = end;
2702 Invalidate();
2706 void CBaseView::OnMergePreviousconflict()
2708 SelectNextBlock(-1, true);
2711 void CBaseView::OnMergeNextconflict()
2713 SelectNextBlock(1, true);
2716 void CBaseView::OnMergeNextdifference()
2718 SelectNextBlock(1, false);
2721 void CBaseView::OnMergePreviousdifference()
2723 SelectNextBlock(-1, false);
2726 bool CBaseView::HasNextConflict()
2728 return SelectNextBlock(1, true, true, true);
2731 bool CBaseView::HasPrevConflict()
2733 return SelectNextBlock(-1, true, true, true);
2736 bool CBaseView::HasNextDiff()
2738 return SelectNextBlock(1, false, true, true);
2741 bool CBaseView::HasPrevDiff()
2743 return SelectNextBlock(-1, false, true, true);
2746 bool CBaseView::LinesInOneChange(int direction,
2747 DiffStates initialLineState, DiffStates currentLineState)
2749 // Checks whether all the adjacent lines starting from the initial line
2750 // and up to the current line form the single change
2752 // First of all, if the two lines have identical states, they surely
2753 // belong to one change.
2754 if (initialLineState == currentLineState)
2755 return true;
2757 // Either we move down and initial line state is "added" or "removed" and
2758 // current line state is "empty"...
2759 if (direction > 0)
2761 if (currentLineState == DIFFSTATE_EMPTY)
2763 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2764 return true;
2766 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2767 return true;
2769 // ...or we move up and initial line state is "empty" and current line
2770 // state is "added" or "removed".
2771 if (direction < 0)
2773 if (initialLineState == DIFFSTATE_EMPTY)
2775 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2776 return true;
2778 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2779 return true;
2781 return false;
2784 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2786 if (! m_pViewData)
2787 return false;
2789 const int linesCount = (int)m_Screen2View.size();
2790 if(linesCount == 0)
2791 return false;
2793 int nCenterPos = GetCaretPosition().y;
2794 int nLimit = -1;
2795 if (nDirection > 0)
2796 nLimit = linesCount;
2798 if (nCenterPos >= linesCount)
2799 nCenterPos = linesCount-1;
2801 if (bSkipEndOfCurrentBlock)
2803 // Find end of current block
2804 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2805 while (nCenterPos != nLimit)
2807 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2808 if (!LinesInOneChange(nDirection, state, lineState))
2809 break;
2810 nCenterPos += nDirection;
2814 // Find next diff/conflict block
2815 while (nCenterPos != nLimit)
2817 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2818 if (!bConflict &&
2819 (linestate != DIFFSTATE_NORMAL) &&
2820 (linestate != DIFFSTATE_UNKNOWN))
2822 break;
2824 if (bConflict &&
2825 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2826 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2827 (linestate == DIFFSTATE_CONFLICTED) ||
2828 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2830 break;
2833 nCenterPos += nDirection;
2835 if (nCenterPos == nLimit)
2836 return false;
2837 if (dryrun)
2838 return (nCenterPos != nLimit);
2840 // Find end of new block
2841 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2842 int nBlockEnd = nCenterPos;
2843 const int maxAllowedLine = nLimit-nDirection;
2844 while (nBlockEnd != maxAllowedLine)
2846 const int lineIndex = nBlockEnd + nDirection;
2847 if (lineIndex >= linesCount)
2848 break;
2849 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2850 if (!LinesInOneChange(nDirection, state, lineState))
2851 break;
2852 nBlockEnd += nDirection;
2855 int nTopPos = nCenterPos - (GetScreenLines()/2);
2856 if (nTopPos < 0)
2857 nTopPos = 0;
2859 POINT ptCaretPos = {0, nCenterPos};
2860 SetCaretPosition(ptCaretPos);
2861 ClearSelection();
2862 if (nDirection > 0)
2863 SetupAllSelection(nCenterPos, nBlockEnd);
2864 else
2865 SetupAllSelection(nBlockEnd, nCenterPos);
2867 ScrollAllToLine(nTopPos, FALSE);
2868 RecalcAllVertScrollBars(TRUE);
2869 SetCaretToLineStart();
2870 EnsureCaretVisible();
2871 OnNavigateNextinlinediff();
2873 UpdateViewsCaretPosition();
2874 UpdateCaret();
2875 ShowDiffLines(nCenterPos);
2876 return true;
2879 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2881 if (pNMHDR->idFrom != (UINT_PTR)m_hWnd)
2882 return FALSE;
2884 CString strTipText;
2885 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2887 DWORD pos = GetMessagePos();
2888 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2889 ScreenToClient(&point);
2890 const int nLine = GetButtonEventLineIndex(point);
2892 if (nLine >= 0)
2894 int nViewLine = GetViewLineForScreen(nLine);
2895 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2897 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2898 if (movedIndex >= 0)
2900 if (m_pViewData->IsMovedFrom(nViewLine))
2902 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2904 else
2906 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2913 *pResult = 0;
2914 if (strTipText.IsEmpty())
2915 return TRUE;
2917 // need to handle both ANSI and UNICODE versions of the message
2918 if (pNMHDR->code == TTN_NEEDTEXTA)
2920 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2921 pTTTA->lpszText = m_szTip;
2922 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2924 else
2926 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2927 lstrcpyn(m_wszTip, strTipText, min(strTipText.GetLength() + 1, _countof(m_wszTip) - 1));
2928 pTTTW->lpszText = m_wszTip;
2931 return TRUE; // message was handled
2934 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2936 CRect rcClient;
2937 GetClientRect(rcClient);
2938 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2939 int marginwidth = MARGINWIDTH;
2940 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2942 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2944 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2946 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2948 // inside the header part of the view (showing the filename)
2949 pTI->hwnd = this->m_hWnd;
2950 this->GetClientRect(&pTI->rect);
2951 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2952 pTI->uId = (UINT_PTR)m_hWnd;
2953 pTI->lpszText = LPSTR_TEXTCALLBACK;
2955 // we want multi line tooltips
2956 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2957 if (pToolTip->GetSafeHwnd() != NULL)
2959 pToolTip->SetMaxTipWidth(INT_MAX);
2962 return (textrect.PtInRect(point) ? 1 : 2);
2965 return -1;
2968 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2970 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2971 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2973 switch (nChar)
2975 case VK_TAB:
2976 if (bControl)
2978 if (this==m_pwndLeft)
2980 if (IsViewGood(m_pwndRight))
2982 m_pwndRight->SetFocus();
2984 else if (IsViewGood(m_pwndBottom))
2986 m_pwndBottom->SetFocus();
2989 else if (this==m_pwndRight)
2991 if (IsViewGood(m_pwndBottom))
2993 m_pwndBottom->SetFocus();
2995 else if (IsViewGood(m_pwndLeft))
2997 m_pwndLeft->SetFocus();
3000 else if (this==m_pwndBottom)
3002 if (IsViewGood(m_pwndLeft))
3004 m_pwndLeft->SetFocus();
3006 else if (IsViewGood(m_pwndRight))
3008 m_pwndRight->SetFocus();
3012 break;
3013 case VK_PRIOR:
3015 POINT ptCaretPos = GetCaretPosition();
3016 ptCaretPos.y -= GetScreenLines();
3017 ptCaretPos.y = max(ptCaretPos.y, 0);
3018 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3019 SetCaretPosition(ptCaretPos);
3020 OnCaretMove(MOVELEFT, bShift);
3021 ShowDiffLines(ptCaretPos.y);
3023 break;
3024 case VK_NEXT:
3026 POINT ptCaretPos = GetCaretPosition();
3027 ptCaretPos.y += GetScreenLines();
3028 if (ptCaretPos.y >= GetLineCount())
3029 ptCaretPos.y = GetLineCount()-1;
3030 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3031 SetCaretPosition(ptCaretPos);
3032 OnCaretMove(MOVERIGHT, bShift);
3033 ShowDiffLines(ptCaretPos.y);
3035 break;
3036 case VK_HOME:
3038 if (bControl)
3040 ScrollAllToLine(0);
3041 SetCaretToViewStart();
3042 m_nCaretGoalPos = 0;
3043 if (bShift)
3044 AdjustSelection(MOVELEFT);
3045 else
3046 ClearSelection();
3047 UpdateCaret();
3049 else
3051 POINT ptCaretPos = GetCaretPosition();
3052 CString sLine = GetLineChars(ptCaretPos.y);
3053 int pos = 0;
3054 while (pos < sLine.GetLength())
3056 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3057 break;
3058 ++pos;
3060 if (ptCaretPos.x == pos)
3062 SetCaretToLineStart();
3063 m_nCaretGoalPos = 0;
3064 OnCaretMove(MOVERIGHT, bShift);
3065 ScrollAllToChar(0);
3067 else
3069 ptCaretPos.x = pos;
3070 SetCaretAndGoalPosition(ptCaretPos);
3071 OnCaretMove(MOVELEFT, bShift);
3075 break;
3076 case VK_END:
3078 if (bControl)
3080 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3081 POINT ptCaretPos;
3082 ptCaretPos.y = GetLineCount()-1;
3083 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3084 SetCaretAndGoalPosition(ptCaretPos);
3085 if (bShift)
3086 AdjustSelection(MOVERIGHT);
3087 else
3088 ClearSelection();
3090 else
3092 POINT ptCaretPos = GetCaretPosition();
3093 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3094 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3096 ptCaretPos.x--;
3098 SetCaretAndGoalPosition(ptCaretPos);
3099 OnCaretMove(MOVERIGHT, bShift);
3102 break;
3103 case VK_BACK:
3104 if (IsWritable())
3106 if (! HasTextSelection())
3108 POINT ptCaretPos = GetCaretPosition();
3109 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3110 break;
3111 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3112 if (bControl)
3113 MoveCaretWordLeft();
3114 else
3116 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3120 m_ptSelectionViewPosStart = GetCaretViewPosition();
3122 RemoveSelectedText();
3124 break;
3125 case VK_DELETE:
3126 if (IsWritable())
3128 if (! HasTextSelection())
3130 if (bControl)
3132 m_ptSelectionViewPosStart = GetCaretViewPosition();
3133 MoveCaretWordRight();
3134 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3136 else
3138 if (! MoveCaretRight())
3139 break;
3140 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3141 MoveCaretLeft();
3142 m_ptSelectionViewPosStart = GetCaretViewPosition();
3145 RemoveSelectedText();
3147 break;
3148 case VK_INSERT:
3149 m_bInsertMode = !m_bInsertMode;
3150 UpdateCaret();
3151 break;
3153 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3156 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3158 const int nClickedLine = GetButtonEventLineIndex(point);
3159 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3161 POINT ptCaretPos;
3162 ptCaretPos.y = nClickedLine;
3163 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3164 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3165 SetCaretAndGoalPosition(ptCaretPos);
3167 if (nFlags & MK_SHIFT)
3168 AdjustSelection(MOVERIGHT);
3169 else
3171 ClearSelection();
3172 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3173 if (point.x < GetMarginWidth())
3175 // select the whole line
3176 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3177 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3181 UpdateViewsCaretPosition();
3182 Invalidate();
3185 CView::OnLButtonDown(nFlags, point);
3188 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3190 if (zChar == ' ' || zChar == '\t' )
3192 return CHG_WHITESPACE;
3194 if (zChar < 0x20)
3196 return CHG_CONTROL;
3198 if (m_sWordSeparators.Find(zChar) >= 0)
3200 return CHG_WORDSEPARATOR;
3202 return CHG_WORDLETTER;
3205 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3207 if (m_pViewData == 0) {
3208 CView::OnLButtonDblClk(nFlags, point);
3209 return;
3212 const int nClickedLine = GetButtonEventLineIndex(point);
3213 if ( nClickedLine < 0)
3214 return;
3215 int nViewLine = GetViewLineForScreen(nClickedLine);
3216 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3218 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3220 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3222 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3223 int screenLine = FindViewLineNumber(movedindex);
3224 int nTop = screenLine - GetScreenLines()/2;
3225 if (nTop < 0)
3226 nTop = 0;
3227 ScrollAllToLine(nTop);
3228 // find and select the whole moved block
3229 int startSel = movedindex;
3230 int endSel = movedindex;
3231 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3232 startSel--;
3233 startSel++;
3234 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3235 endSel++;
3236 endSel--;
3237 m_pOtherView->SetupSelection(startSel, endSel);
3238 return CView::OnLButtonDblClk(nFlags, point);
3242 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3244 // a double click on a marker expands the hidden text
3245 int i = nViewLine;
3246 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3248 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3249 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3250 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3251 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3252 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3253 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3254 i++;
3256 BuildAllScreen2ViewVector();
3257 if (m_pwndLeft)
3258 m_pwndLeft->Invalidate();
3259 if (m_pwndRight)
3260 m_pwndRight->Invalidate();
3261 if (m_pwndBottom)
3262 m_pwndBottom->Invalidate();
3264 else
3266 POINT ptCaretPos;
3267 ptCaretPos.y = nClickedLine;
3268 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3269 SetCaretPosition(ptCaretPos);
3270 ClearSelection();
3272 POINT ptViewCarret = GetCaretViewPosition();
3273 nViewLine = ptViewCarret.y;
3274 if (nViewLine >= GetViewCount())
3275 return;
3276 const CString &sLine = GetViewLine(nViewLine);
3277 int nLineLength = sLine.GetLength();
3278 int nBasePos = ptViewCarret.x;
3279 // get target char group
3280 ECharGroup eLeft = CHG_UNKNOWN;
3281 if (nBasePos > 0)
3283 eLeft = GetCharGroup(sLine[nBasePos-1]);
3285 ECharGroup eRight = CHG_UNKNOWN;
3286 if (nBasePos < nLineLength)
3288 eRight = GetCharGroup(sLine[nBasePos]);
3290 ECharGroup eTarget = max(eRight, eLeft);
3291 // find left margin
3292 int nLeft = nBasePos;
3293 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3295 nLeft--;
3297 // get right margin
3298 int nRight = nBasePos;
3299 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3301 nRight++;
3303 // set selection
3304 m_ptSelectionViewPosStart.x = nLeft;
3305 m_ptSelectionViewPosStart.y = nViewLine;
3306 m_ptSelectionViewPosEnd.x = nRight;
3307 m_ptSelectionViewPosEnd.y = nViewLine;
3308 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3309 SetupAllViewSelection(nViewLine, nViewLine);
3310 // set caret
3311 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3312 UpdateViewsCaretPosition();
3313 UpdateGoalPos();
3315 // set mark word
3316 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3317 int nMarkWidth = max(nRight - nLeft, 0);
3318 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3319 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3321 m_sMarkedWord.Empty();
3324 if (m_pwndLeft)
3325 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3326 if (m_pwndRight)
3327 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3328 if (m_pwndBottom)
3329 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3331 Invalidate();
3332 if (m_pwndLocator)
3333 m_pwndLocator->Invalidate();
3336 CView::OnLButtonDblClk(nFlags, point);
3339 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3341 const int nClickedLine = GetButtonEventLineIndex(point);
3342 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3344 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3346 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3347 if (pidl)
3349 SHOpenFolderAndSelectItems(pidl,0,0,0);
3350 CoTaskMemFree((LPVOID)pidl);
3353 return;
3355 POINT ptCaretPos;
3356 ptCaretPos.y = nClickedLine;
3357 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3358 SetCaretAndGoalPosition(ptCaretPos);
3359 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3360 if (m_pwndLeft)
3361 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3362 if (m_pwndRight)
3363 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3364 if (m_pwndBottom)
3365 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3366 ClearSelection();
3367 m_ptSelectionViewPosStart.x = 0;
3368 m_ptSelectionViewPosStart.y = nClickedLine;
3369 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3370 m_ptSelectionViewPosEnd.y = nClickedLine;
3371 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3372 UpdateViewsCaretPosition();
3373 Invalidate();
3374 if (m_pwndLocator)
3375 m_pwndLocator->Invalidate();
3378 void CBaseView::OnEditCopy()
3380 CString sCopyData = GetSelectedText();
3382 if (!sCopyData.IsEmpty())
3384 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3388 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3390 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3392 --m_pMainFrame->m_nMoveMovesToIgnore;
3393 CView::OnMouseMove(nFlags, point);
3394 return;
3396 int nMouseLine = GetButtonEventLineIndex(point);
3397 if (nMouseLine < -1)
3398 nMouseLine = -1;
3399 m_mouseInMargin = point.x < GetMarginWidth();
3401 ShowDiffLines(nMouseLine);
3403 KillTimer(IDT_SCROLLTIMER);
3404 if (nFlags & MK_LBUTTON)
3406 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3407 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3408 if (saveMouseLine < 0)
3409 return;
3410 int col = CalcColFromPoint(point.x, saveMouseLine);
3411 int charIndex = CalculateCharIndex(saveMouseLine, col);
3412 if (HasSelection() &&
3413 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3415 POINT ptCaretPos = {charIndex, nMouseLine};
3416 SetCaretAndGoalPosition(ptCaretPos);
3417 AdjustSelection(MOVERIGHT);
3418 Invalidate();
3419 UpdateWindow();
3421 if (nMouseLine < m_nTopLine)
3423 ScrollAllToLine(m_nTopLine-1, TRUE);
3424 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3426 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3428 ScrollAllToLine(m_nTopLine+1, TRUE);
3429 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3431 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3433 ScrollAllSide(-1);
3434 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3436 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3438 ScrollAllSide(1);
3439 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3441 SetCapture();
3445 CView::OnMouseMove(nFlags, point);
3448 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3450 ShowDiffLines(-1);
3451 ReleaseCapture();
3452 KillTimer(IDT_SCROLLTIMER);
3454 __super::OnLButtonUp(nFlags, point);
3457 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3459 if (nIDEvent == IDT_SCROLLTIMER)
3461 POINT point;
3462 GetCursorPos(&point);
3463 ScreenToClient(&point);
3464 int nMouseLine = GetButtonEventLineIndex(point);
3465 if (nMouseLine < -1)
3467 nMouseLine = -1;
3469 if (GetKeyState(VK_LBUTTON)&0x8000)
3471 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3472 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3473 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3474 if (nMouseLine < m_nTopLine)
3476 ScrollAllToLine(m_nTopLine-1, TRUE);
3477 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3479 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3481 ScrollAllToLine(m_nTopLine+1, TRUE);
3482 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3484 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3486 ScrollAllSide(-1);
3487 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3489 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3491 ScrollAllSide(1);
3492 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3498 CView::OnTimer(nIDEvent);
3501 void CBaseView::ShowDiffLines(int nLine)
3503 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3505 m_pwndLineDiffBar->ShowLines(nLine);
3506 nLine = -1;
3507 m_nMouseLine = nLine;
3508 return;
3511 if ((!m_pwndRight)||(!m_pwndLeft))
3512 return;
3513 if(m_pMainFrame->m_bOneWay)
3514 return;
3516 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3517 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3519 if (nLine < 0)
3520 return;
3522 if (nLine != m_nMouseLine)
3524 if (nLine >= GetLineCount())
3525 nLine = -1;
3526 m_nMouseLine = nLine;
3527 m_pwndLineDiffBar->ShowLines(nLine);
3529 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3532 const viewdata& CBaseView::GetEmptyLineData()
3534 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3535 return emptyLine;
3538 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3540 for (int i = 0; i < nCount; i++)
3542 InsertViewData(nFirstView, GetEmptyLineData());
3547 void CBaseView::UpdateCaret()
3549 POINT ptCaretPos = GetCaretPosition();
3550 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3551 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3552 SetCaretPosition(ptCaretPos);
3554 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3556 if (m_bFocused &&
3557 ptCaretPos.y >= m_nTopLine &&
3558 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3559 nCaretOffset >= m_nOffsetChar &&
3560 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3562 POINT pt1 = TextToClient(ptCaretPos);
3563 if (m_bInsertMode)
3564 CreateSolidCaret(2, GetLineHeight());
3565 else
3567 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3568 POINT pt2 = TextToClient(pt);
3569 int width = max(GetCharWidth(), pt2.x - pt1.x);
3570 CreateSolidCaret(width, GetLineHeight());
3572 SetCaretPos(pt1);
3573 ShowCaret();
3575 else
3577 HideCaret();
3581 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3583 POINT ptViewPos;
3584 ptViewPos.x = pt.x;
3586 int nSubLine = GetSubLineOffset(pt.y);
3587 if (nSubLine > 0)
3589 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3591 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3595 ptViewPos.y = GetViewLineForScreen(pt.y);
3596 return ptViewPos;
3599 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3601 POINT ptPos;
3602 int nViewLineLenLeft = GetViewLineLength(pt.y);
3603 ptPos.x = min(nViewLineLenLeft, pt.x);
3604 ptPos.y = FindScreenLineForViewLine(pt.y);
3605 if (GetViewLineForScreen(ptPos.y) != pt.y )
3607 ptPos.x = 0;
3609 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3611 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3612 while (nSubLineLength < ptPos.x)
3614 ptPos.x -= nSubLineLength;
3615 nViewLineLenLeft -= nSubLineLength;
3616 ptPos.y++;
3617 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3619 // last pos of non last sub-line go to start of next screen line
3620 // Note: while this works correctly, it's not what a user might expect:
3621 // cursor-right when the caret is before the last char of a wrapped line
3622 // now moves the caret to the next line. But users expect the caret to
3623 // move to the right of the last char instead, and with another cursor-right
3624 // keystroke to move the caret to the next line.
3625 // Basically, this would require to handle two caret positions for the same
3626 // logical position in the line string (one on the last position of the first line,
3627 // one on the first position of the new line. For non-wrapped lines this works
3628 // because there's an 'invisible' newline char at the end of the first line.
3629 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3631 ptPos.x = 0;
3632 ptPos.y++;
3636 return ptPos;
3640 void CBaseView::EnsureCaretVisible()
3642 POINT ptCaretPos = GetCaretPosition();
3643 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3645 if (ptCaretPos.y < m_nTopLine)
3646 ScrollAllToLine(ptCaretPos.y);
3647 int screnLines = GetScreenLines();
3648 if (screnLines)
3650 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3651 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3652 if (nCaretOffset < m_nOffsetChar)
3653 ScrollAllToChar(nCaretOffset);
3654 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3655 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3659 int CBaseView::CalculateActualOffset(const POINT& point)
3661 int nLineIndex = point.y;
3662 int nCharIndex = point.x;
3663 ASSERT(nCharIndex >= 0);
3664 CString sLine = GetLineChars(nLineIndex);
3665 int nLineLength = sLine.GetLength();
3666 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3669 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3671 int nLength = GetLineLength(nLineIndex);
3672 int nSubLine = GetSubLineOffset(nLineIndex);
3673 if (nSubLine>=0)
3675 int nViewLine = GetViewLineForScreen(nLineIndex);
3676 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3678 int nMultilineCount = CountMultiLines(nViewLine);
3679 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3681 nLength--;
3685 CString Line = GetLineChars(nLineIndex);
3686 int nIndex = 0;
3687 int nOffset = 0;
3688 int nTabSize = GetTabSize();
3689 while (nOffset < nActualOffset && nIndex < nLength)
3691 if (Line.GetAt(nIndex) == _T('\t'))
3692 nOffset += (nTabSize - nOffset % nTabSize);
3693 else
3694 ++nOffset;
3695 ++nIndex;
3697 return nIndex;
3701 * @param xpos X coordinate in CBaseView
3702 * @param lineIndex logical line index (e.g. wrap/collapse)
3704 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3706 int xpos2;
3707 CDC *pDC = GetDC();
3708 if (pDC != nullptr)
3710 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3711 int fit = text.GetLength();
3712 auto posBuffer = std::make_unique<int[]>(fit);
3713 pDC->SelectObject(GetFont()); // is this right font ?
3714 SIZE size;
3715 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3716 ReleaseDC(pDC);
3717 int lower = -1, upper = fit - 1;
3718 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3721 int middle = (upper + lower + 1) / 2;
3722 int width = posBuffer.get()[middle];
3723 if (xcheck < width)
3724 upper = middle - 1;
3725 else
3726 lower = middle;
3727 } while (lower < upper);
3728 lower++;
3729 xpos2 = lower;
3730 if (lower < fit - 1)
3732 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3733 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3734 xpos2++;
3737 else
3739 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3740 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3741 xpos2++;
3743 return xpos2;
3746 POINT CBaseView::TextToClient(const POINT& point)
3748 POINT pt;
3749 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3750 pt.y = nOffsetScreenLine * GetLineHeight();
3751 pt.x = CalculateActualOffset(point);
3753 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3754 CDC * pDC = GetDC();
3755 if (pDC)
3757 pDC->SelectObject(GetFont()); // is this right font ?
3758 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3759 CString sLine = GetLineChars(nScreenLine);
3760 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3761 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3762 ReleaseDC(pDC);
3763 } else {
3764 nLeft += pt.x * GetCharWidth();
3767 pt.x = nLeft;
3768 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3769 return pt;
3772 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3774 CView::OnChar(nChar, nRepCnt, nFlags);
3776 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3777 bool bSkipSelectionClear = false;
3779 if (IsReadonly())
3780 return;
3782 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3783 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3785 return;
3788 if (!m_pViewData) // no data - nothing to do
3789 return;
3791 if (nChar == VK_F16)
3793 // generated by a ctrl+backspace - ignore.
3795 else if (nChar==VK_TAB && HasTextLineSelection())
3797 // change indentation for selected lines
3798 if (bShift)
3800 RemoveIndentationForSelectedBlock();
3802 else
3804 AddIndentationForSelectedBlock();
3806 bSkipSelectionClear = true;
3808 else if ((nChar > 31)||(nChar == VK_TAB))
3810 ResetUndoStep();
3811 RemoveSelectedText();
3812 POINT ptCaretViewPos = GetCaretViewPosition();
3813 int nViewLine = ptCaretViewPos.y;
3814 if ((nViewLine==0)&&(GetViewCount()==0))
3815 OnChar(VK_RETURN, 0, 0);
3816 int charCount = 1;
3817 viewdata lineData = GetViewData(nViewLine);
3818 if (nChar == VK_TAB)
3820 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3821 if (indentChars > 0)
3823 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3824 charCount = indentChars;
3826 else
3827 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3829 else
3831 if (m_bInsertMode)
3832 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3833 else
3835 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3836 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3837 else
3838 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3841 if (IsStateEmpty(lineData.state))
3843 // if not last line set EOL
3844 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3846 if (!IsViewLineEmpty(nCheckViewLine))
3848 lineData.ending = m_lineendings;
3849 break;
3852 // make sure previous (non empty) line have EOL set
3853 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3855 if (!IsViewLineEmpty(nCheckViewLine))
3857 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3859 SetViewLineEnding(nCheckViewLine, m_lineendings);
3861 break;
3865 lineData.state = DIFFSTATE_EDITED;
3866 bool bNeedRenumber = false;
3867 if (lineData.linenumber == -1)
3869 lineData.linenumber = 0;
3870 bNeedRenumber = true;
3872 SetViewData(nViewLine, lineData);
3873 SetModified();
3874 SaveUndoStep();
3875 BuildAllScreen2ViewVector(nViewLine);
3876 if (bNeedRenumber)
3878 UpdateViewLineNumbers();
3880 for (int i = 0; i < charCount; ++i)
3881 MoveCaretRight();
3882 UpdateGoalPos();
3884 else if (nChar == 10)
3886 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3887 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3888 EOL newEOL = EOL_CRLF;
3889 switch (eol)
3891 case EOL_CRLF:
3892 newEOL = EOL_CR;
3893 break;
3894 case EOL_CR:
3895 newEOL = EOL_LF;
3896 break;
3897 case EOL_LF:
3898 newEOL = EOL_CRLF;
3899 break;
3901 if (eol==EOL_NOENDING || eol==newEOL)
3902 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3903 // to add EOL on newly edited empty line hit enter
3904 // don't store into UNDO if no change happened
3905 // and don't mark file as modified
3906 return;
3907 AddUndoViewLine(nViewLine);
3908 m_pViewData->SetLineEnding(nViewLine, newEOL);
3909 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3910 UpdateGoalPos();
3912 else if (nChar == VK_RETURN)
3914 // insert a new, fresh and empty line below the cursor
3915 RemoveSelectedText();
3917 CUndo::GetInstance().BeginGrouping();
3919 POINT ptCaretViewPos = GetCaretViewPosition();
3920 int nViewLine = ptCaretViewPos.y;
3921 int nLeft = ptCaretViewPos.x;
3922 CString sLine = GetViewLineChars(nViewLine);
3923 CString sLineLeft = sLine.Left(nLeft);
3924 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3925 EOL eOriginalEnding = EOL_AUTOLINE;
3926 if (m_pViewData->GetCount() > nViewLine)
3927 eOriginalEnding = GetViewLineEnding(nViewLine);
3929 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3931 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3932 SetViewData(nViewLine, newFirstLine);
3935 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3936 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3937 InsertViewData(nInsertLine, newLastLine);
3938 SetModified();
3939 SaveUndoStep();
3941 // adds new line everywhere except me
3942 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3944 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3946 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3948 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3950 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3952 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3954 SaveUndoStep();
3956 UpdateViewLineNumbers();
3957 SaveUndoStep();
3958 CUndo::GetInstance().EndGrouping();
3960 BuildAllScreen2ViewVector();
3961 // move the cursor to the new line
3962 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3963 SetCaretAndGoalViewPosition(ptCaretViewPos);
3965 else
3966 return; // Unknown control character -- ignore it.
3967 if (!bSkipSelectionClear)
3968 ClearSelection();
3969 EnsureCaretVisible();
3970 UpdateCaret();
3971 Invalidate(FALSE);
3974 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3976 ResetUndoStep();
3977 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3978 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3979 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3980 SetModified();
3981 SaveUndoStep();
3982 RecalcAllVertScrollBars();
3983 Invalidate(FALSE);
3986 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3988 if (m_pViewData == NULL)
3989 return;
3990 int viewLine = nViewLineIndex;
3991 EOL ending = m_pViewData->GetLineEnding(viewLine);
3992 if (ending == EOL_NOENDING)
3994 ending = m_lineendings;
3996 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3997 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3999 CString sPartLine = GetViewLineChars(nViewLineIndex);
4000 int nPosx = GetCaretPosition().x; // should be view pos ?
4001 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
4002 sPartLine = sPartLine.Mid(nPosx);
4003 newLine.sLine = sPartLine;
4005 m_pViewData->InsertData(viewLine+1, newLine);
4006 BuildAllScreen2ViewVector();
4009 void CBaseView::RemoveSelectedText()
4011 if (m_pViewData == NULL)
4012 return;
4013 if (!HasTextSelection())
4014 return;
4016 // fix selection if starts or ends on empty line
4017 SetCaretViewPosition(m_ptSelectionViewPosEnd);
4018 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4021 m_ptSelectionViewPosEnd = GetCaretViewPosition();
4022 SetCaretViewPosition(m_ptSelectionViewPosStart);
4023 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4026 m_ptSelectionViewPosStart = GetCaretViewPosition();
4027 if (!HasTextSelection())
4029 ClearSelection();
4030 return;
4033 // We want to undo the insertion in a single step.
4034 ResetUndoStep();
4035 CUndo::GetInstance().BeginGrouping();
4037 // combine first and last line
4038 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
4039 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
4040 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
4041 oFirstLine.ending = oLastLine.ending;
4042 oFirstLine.state = DIFFSTATE_EDITED;
4043 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4045 // clean up middle lines if any
4046 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4048 viewdata oEmptyLine = GetEmptyLineData();
4049 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4051 SetViewData(nViewLine, oEmptyLine);
4053 SaveUndoStep();
4055 if (CleanEmptyLines())
4057 BuildAllScreen2ViewVector(); // schedule full rebuild
4059 SaveUndoStep();
4060 UpdateViewLineNumbers();
4063 SetModified(); //TODO set modified only if real data was changed
4064 SaveUndoStep();
4065 CUndo::GetInstance().EndGrouping();
4067 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4068 SetCaretViewPosition(m_ptSelectionViewPosStart);
4069 UpdateGoalPos();
4070 ClearSelection();
4071 UpdateCaret();
4072 EnsureCaretVisible();
4073 Invalidate(FALSE);
4076 void CBaseView::PasteText()
4078 if (!OpenClipboard())
4079 return;
4081 CString sClipboardText;
4082 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4083 if (hglb)
4085 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4086 sClipboardText = CString(lpstr);
4087 GlobalUnlock(hglb);
4089 hglb = GetClipboardData(CF_UNICODETEXT);
4090 if (hglb)
4092 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4093 sClipboardText = lpstr;
4094 GlobalUnlock(hglb);
4096 CloseClipboard();
4098 if (sClipboardText.IsEmpty())
4099 return;
4101 sClipboardText.Replace(_T("\r\n"), _T("\r"));
4102 sClipboardText.Replace('\n', '\r');
4104 InsertText(sClipboardText);
4107 void CBaseView::OnCaretDown()
4109 POINT ptCaretPos = GetCaretPosition();
4110 int nLine = ptCaretPos.y;
4111 int nNextLine = nLine + 1;
4112 if (nNextLine >= GetLineCount()) // already at last line
4114 return;
4117 POINT ptCaretViewPos = GetCaretViewPosition();
4118 int nViewLine = ptCaretViewPos.y;
4119 int nNextViewLine = GetViewLineForScreen(nNextLine);
4120 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4122 // find next suitable screen line
4123 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4125 nNextLine++;
4126 if (nNextLine >= GetLineCount())
4128 return;
4130 nNextViewLine = GetViewLineForScreen(nNextLine);
4133 ptCaretPos.y = nNextLine;
4134 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4135 SetCaretPosition(ptCaretPos);
4136 OnCaretMove(MOVELEFT);
4137 ShowDiffLines(ptCaretPos.y);
4140 bool CBaseView::MoveCaretLeft()
4142 POINT ptCaretViewPos = GetCaretViewPosition();
4144 //int nViewLine = ptCaretViewPos.y;
4145 if (ptCaretViewPos.x == 0)
4147 int nPrevLine = GetCaretPosition().y;
4148 int nPrevViewLine;
4149 do {
4150 nPrevLine--;
4151 if (nPrevLine < 0)
4153 return false;
4155 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4156 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4157 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4158 ShowDiffLines(nPrevLine);
4160 else
4161 --ptCaretViewPos.x;
4163 SetCaretAndGoalViewPosition(ptCaretViewPos);
4164 return true;
4167 bool CBaseView::MoveCaretRight()
4169 POINT ptCaretViewPos = GetCaretViewPosition();
4171 int nViewLine = ptCaretViewPos.y;
4172 int nViewLineLen = GetViewLineLength(nViewLine);
4173 if (ptCaretViewPos.x >= nViewLineLen)
4175 int nNextLine = GetCaretPosition().y;
4176 int nNextViewLine;
4177 do {
4178 nNextLine++;
4179 if (nNextLine >= GetLineCount())
4181 return false;
4183 nNextViewLine = GetViewLineForScreen(nNextLine);
4184 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4185 ptCaretViewPos.y = nNextViewLine;
4186 ptCaretViewPos.x = 0;
4187 ShowDiffLines(nNextLine);
4189 else
4190 ++ptCaretViewPos.x;
4192 SetCaretAndGoalViewPosition(ptCaretViewPos);
4193 return true;
4196 void CBaseView::UpdateGoalPos()
4198 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4201 void CBaseView::OnCaretLeft()
4203 MoveCaretLeft();
4204 OnCaretMove(MOVELEFT);
4207 void CBaseView::OnCaretRight()
4209 MoveCaretRight();
4210 OnCaretMove(MOVERIGHT);
4213 void CBaseView::OnCaretUp()
4215 POINT ptCaretPos = GetCaretPosition();
4216 int nLine = ptCaretPos.y;
4217 if (nLine <= 0) // already at first line
4219 return;
4221 int nPrevLine = nLine - 1;
4223 POINT ptCaretViewPos = GetCaretViewPosition();
4224 int nViewLine = ptCaretViewPos.y;
4225 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4226 if (nPrevViewLine != nViewLine) // not on same view line
4228 // find previous suitable screen line
4229 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4231 if (nPrevLine <= 0)
4233 return;
4235 nPrevLine--;
4236 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4239 ptCaretPos.y = nPrevLine;
4240 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4241 SetCaretPosition(ptCaretPos);
4242 OnCaretMove(MOVELEFT);
4243 ShowDiffLines(ptCaretPos.y);
4246 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4248 switch (GetCharGroup(ch))
4250 case CHG_CONTROL:
4251 case CHG_WHITESPACE:
4252 case CHG_WORDSEPARATOR:
4253 return true;
4255 return false;
4258 bool CBaseView::IsCaretAtWordBoundary()
4260 POINT ptViewCaret = GetCaretViewPosition();
4261 CString line = GetViewLineChars(ptViewCaret.y);
4262 if (line.IsEmpty())
4263 return false; // no boundary at the empty lines
4264 if (ptViewCaret.x == 0)
4265 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4266 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4267 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4268 return
4269 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4270 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4273 void CBaseView::UpdateViewsCaretPosition()
4275 POINT ptCaretPos = GetCaretPosition();
4276 if (m_pwndBottom && m_pwndBottom!=this)
4277 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4278 if (m_pwndLeft && m_pwndLeft!=this)
4279 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4280 if (m_pwndRight && m_pwndRight!=this)
4281 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4284 void CBaseView::OnCaretWordleft()
4286 MoveCaretWordLeft();
4287 OnCaretMove(MOVELEFT);
4290 void CBaseView::OnCaretWordright()
4292 MoveCaretWordRight();
4293 OnCaretMove(MOVERIGHT);
4296 void CBaseView::MoveCaretWordLeft()
4298 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4303 void CBaseView::MoveCaretWordRight()
4305 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4310 void CBaseView::ClearCurrentSelection()
4312 m_ptSelectionViewPosStart = GetCaretViewPosition();
4313 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4314 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4315 m_nSelViewBlockStart = -1;
4316 m_nSelViewBlockEnd = -1;
4317 Invalidate(FALSE);
4320 void CBaseView::ClearSelection()
4322 if (m_pwndLeft)
4323 m_pwndLeft->ClearCurrentSelection();
4324 if (m_pwndRight)
4325 m_pwndRight->ClearCurrentSelection();
4326 if (m_pwndBottom)
4327 m_pwndBottom->ClearCurrentSelection();
4330 void CBaseView::AdjustSelection(bool bMoveLeft)
4332 POINT ptCaretViewPos = GetCaretViewPosition();
4333 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4335 // select all have been used recently update origin
4336 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4338 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4339 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4341 m_ptSelectionViewPosStart = ptCaretViewPos;
4342 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4344 else
4346 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4347 m_ptSelectionViewPosEnd = ptCaretViewPos;
4350 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4352 Invalidate(FALSE);
4355 void CBaseView::OnEditCut()
4357 if (IsWritable())
4359 OnEditCopy();
4360 RemoveSelectedText();
4364 void CBaseView::OnEditPaste()
4366 if (IsWritable())
4368 CUndo::GetInstance().BeginGrouping();
4369 RemoveSelectedText();
4370 PasteText();
4371 CUndo::GetInstance().EndGrouping();
4375 void CBaseView::DeleteFonts()
4377 for (int i=0; i<fontsCount; i++)
4379 if (m_apFonts[i] != NULL)
4381 m_apFonts[i]->DeleteObject();
4382 delete m_apFonts[i];
4383 m_apFonts[i] = NULL;
4388 void CBaseView::OnCaretMove(bool bMoveLeft)
4390 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4391 OnCaretMove(bMoveLeft, bShift);
4394 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4396 if(isShiftPressed)
4397 AdjustSelection(bMoveLeft);
4398 else
4399 ClearSelection();
4400 EnsureCaretVisible();
4401 UpdateCaret();
4404 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4406 AddCutCopyAndPaste(popup);
4409 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4411 popup.AppendMenu(MF_SEPARATOR, NULL);
4412 CString temp;
4413 temp.LoadString(IDS_EDIT_COPY);
4414 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4415 if (IsWritable())
4417 temp.LoadString(IDS_EDIT_CUT);
4418 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4419 temp.LoadString(IDS_EDIT_PASTE);
4420 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4421 popup.AppendMenu(MF_SEPARATOR, NULL);
4425 void CBaseView::CompensateForKeyboard(CPoint& point)
4427 // if the context menu is invoked through the keyboard, we have to use
4428 // a calculated position on where to anchor the menu on
4429 if (ArePointsSame(point, SetupPoint(-1, -1)))
4431 CRect rect;
4432 GetWindowRect(&rect);
4433 point = rect.CenterPoint();
4437 HICON CBaseView::LoadIcon(WORD iconId)
4439 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4440 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4441 return (HICON)icon;
4444 void CBaseView::ReleaseBitmap()
4446 if (m_pCacheBitmap != NULL)
4448 m_pCacheBitmap->DeleteObject();
4449 delete m_pCacheBitmap;
4450 m_pCacheBitmap = NULL;
4454 void CBaseView::BuildMarkedWordArray()
4456 int lineCount = GetLineCount();
4457 m_arMarkedWordLines.clear();
4458 m_arMarkedWordLines.reserve(lineCount);
4459 bool bDoit = !m_sMarkedWord.IsEmpty();
4460 for (int i = 0; i < lineCount; ++i)
4462 if (bDoit)
4464 CString line = GetLineChars(i);
4466 if (!line.IsEmpty())
4468 int found = 0;
4469 int nMarkStart = -1;
4470 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4472 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4473 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4474 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4475 if (eLeft != eStart)
4477 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4478 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4479 if (eRight != eEnd)
4481 found = 1;
4482 break;
4486 m_arMarkedWordLines.push_back(found);
4488 else
4489 m_arMarkedWordLines.push_back(0);
4491 else
4492 m_arMarkedWordLines.push_back(0);
4496 void CBaseView::BuildFindStringArray()
4498 int lineCount = GetLineCount();
4499 m_arFindStringLines.clear();
4500 m_arFindStringLines.reserve(lineCount);
4501 bool bDoit = !m_sFindText.IsEmpty();
4502 int s = 0;
4503 int e = 0;
4504 for (int i = 0; i < lineCount; ++i)
4506 if (bDoit)
4508 CString line = GetLineChars(i);
4510 if (!line.IsEmpty())
4512 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4514 case DIFFSTATE_EMPTY:
4515 m_arFindStringLines.push_back(0);
4516 break;
4517 case DIFFSTATE_UNKNOWN:
4518 case DIFFSTATE_NORMAL:
4519 if (m_bLimitToDiff)
4521 m_arFindStringLines.push_back(0);
4522 break;
4524 case DIFFSTATE_REMOVED:
4525 case DIFFSTATE_REMOVEDWHITESPACE:
4526 case DIFFSTATE_ADDED:
4527 case DIFFSTATE_ADDEDWHITESPACE:
4528 case DIFFSTATE_WHITESPACE:
4529 case DIFFSTATE_WHITESPACE_DIFF:
4530 case DIFFSTATE_CONFLICTED:
4531 case DIFFSTATE_CONFLICTED_IGNORED:
4532 case DIFFSTATE_CONFLICTADDED:
4533 case DIFFSTATE_CONFLICTEMPTY:
4534 case DIFFSTATE_CONFLICTRESOLVED:
4535 case DIFFSTATE_IDENTICALREMOVED:
4536 case DIFFSTATE_IDENTICALADDED:
4537 case DIFFSTATE_THEIRSREMOVED:
4538 case DIFFSTATE_THEIRSADDED:
4539 case DIFFSTATE_YOURSREMOVED:
4540 case DIFFSTATE_YOURSADDED:
4541 case DIFFSTATE_EDITED:
4543 if (!m_bMatchCase)
4544 line = line.MakeLower();
4545 s = 0;
4546 e = 0;
4547 int match = 0;
4548 while (StringFound(line, SearchNext, s, e))
4550 match++;
4551 s = e;
4552 e = 0;
4554 m_arFindStringLines.push_back(match);
4555 break;
4557 default:
4558 m_arFindStringLines.push_back(0);
4561 else
4562 m_arFindStringLines.push_back(0);
4564 else
4565 m_arFindStringLines.push_back(0);
4567 UpdateLocator();
4570 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4572 if (!m_bShowInlineDiff)
4573 return false;
4574 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4575 return false;
4577 if (m_pViewData == nullptr || m_pViewData->GetCount() <= nViewLine)
4578 return false;
4579 const CString &sLine = m_pViewData->GetLine(nViewLine);
4580 if (sLine.IsEmpty())
4581 return false;
4583 CheckOtherView();
4584 if (!m_pOtherViewData)
4585 return false;
4587 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4588 if (sDiffLine.IsEmpty())
4589 return false;
4591 svn_diff_t * diff = NULL;
4592 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4593 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4594 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4595 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4596 return false;
4598 size_t lineoffset = 0;
4599 size_t position = 0;
4600 while (diff)
4602 if (this == m_pwndRight)
4604 apr_off_t nTmp = diff->modified_length;
4605 diff->modified_length = diff->original_length;
4606 diff->original_length = nTmp;
4608 nTmp = diff->modified_start;
4609 diff->modified_start = diff->original_start;
4610 diff->original_start = nTmp;
4612 apr_off_t len = diff->original_length;
4613 size_t oldpos = position;
4615 for (apr_off_t i = 0; i < len; ++i)
4617 position += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4618 lineoffset++;
4621 if (diff->type == svn_diff__type_diff_modified)
4623 inlineDiffPos p;
4624 p.start = oldpos;
4625 p.end = position;
4626 positions.push_back(p);
4629 diff = diff->next;
4632 return !positions.empty();
4635 void CBaseView::OnNavigateNextinlinediff()
4637 int nX;
4638 if (GetNextInlineDiff(nX))
4640 POINT ptCaretViewPos = GetCaretViewPosition();
4641 ptCaretViewPos.x = nX;
4642 SetCaretAndGoalViewPosition(ptCaretViewPos);
4643 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4644 EnsureCaretVisible();
4648 void CBaseView::OnNavigatePrevinlinediff()
4650 int nX;
4651 if (GetPrevInlineDiff(nX))
4653 POINT ptCaretViewPos = GetCaretViewPosition();
4654 ptCaretViewPos.x = nX;
4655 SetCaretAndGoalViewPosition(ptCaretViewPos);
4656 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4657 EnsureCaretVisible();
4661 bool CBaseView::HasNextInlineDiff()
4663 int nPos;
4664 return GetNextInlineDiff(nPos);
4667 bool CBaseView::GetNextInlineDiff(int & nPos)
4669 POINT ptCaretViewPos = GetCaretViewPosition();
4670 std::vector<inlineDiffPos> positions;
4671 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4673 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4675 if (it->start > ptCaretViewPos.x)
4677 nPos = (LONG)it->start;
4678 return true;
4680 if (it->end > ptCaretViewPos.x)
4682 nPos = (LONG)it->end;
4683 return true;
4687 return false;
4690 bool CBaseView::HasPrevInlineDiff()
4692 int nPos;
4693 return GetPrevInlineDiff(nPos);
4696 bool CBaseView::GetPrevInlineDiff(int & nPos)
4698 POINT ptCaretViewPos = GetCaretViewPosition();
4699 std::vector<inlineDiffPos> positions;
4700 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4702 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4704 if ( it->end < ptCaretViewPos.x)
4706 nPos = (LONG)it->end;
4707 return true;
4709 if ( it->start < ptCaretViewPos.x)
4711 nPos = (LONG)it->start;
4712 return true;
4716 return false;
4719 CBaseView * CBaseView::GetFirstGoodView()
4721 if (IsViewGood(m_pwndLeft))
4722 return m_pwndLeft;
4723 if (IsViewGood(m_pwndRight))
4724 return m_pwndRight;
4725 if (IsViewGood(m_pwndBottom))
4726 return m_pwndBottom;
4727 return NULL;
4730 void CBaseView::BuildAllScreen2ViewVector()
4732 CBaseView * p_pwndView = GetFirstGoodView();
4733 if (p_pwndView)
4735 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4739 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4741 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4744 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4746 CBaseView * p_pwndView = GetFirstGoodView();
4747 if (p_pwndView)
4749 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4753 void CBaseView::UpdateViewLineNumbers()
4755 int nLineNumber = 0;
4756 int nViewLineCount = GetViewCount();
4757 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4759 int oldLine = (int)GetViewLineNumber(nViewLine);
4760 if (oldLine >= 0)
4761 SetViewLineNumber(nViewLine, nLineNumber++);
4763 m_nDigits = 0;
4766 int CBaseView::CleanEmptyLines()
4768 int nRemovedCount = 0;
4769 int nViewLineCount = GetViewCount();
4770 bool bCheckLeft = IsViewGood(m_pwndLeft);
4771 bool bCheckRight = IsViewGood(m_pwndRight);
4772 bool bCheckBottom = IsViewGood(m_pwndBottom);
4773 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4775 bool bAllEmpty = true;
4776 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4777 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4778 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4779 if (bAllEmpty)
4781 if (bCheckLeft)
4783 m_pwndLeft->RemoveViewData(nViewLine);
4785 if (bCheckRight)
4787 m_pwndRight->RemoveViewData(nViewLine);
4789 if (bCheckBottom)
4791 m_pwndBottom->RemoveViewData(nViewLine);
4793 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4795 SaveUndoStep();
4797 nViewLineCount--;
4798 nRemovedCount++;
4799 continue;
4801 nViewLine++;
4803 return nRemovedCount;
4806 int CBaseView::FindScreenLineForViewLine( int viewLine )
4808 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4811 int CBaseView::CountMultiLines( int nViewLine )
4813 if (m_ScreenedViewLine.empty())
4814 return 0; // in case the view is completely empty
4816 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4818 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4820 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4823 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4825 TScreenedViewLine oScreenedLine;
4826 // tokenize string
4827 int prevpos = 0;
4828 int pos = 0;
4829 while ((pos = multiline.Find('\n', pos)) >= 0)
4831 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4832 pos++;
4833 prevpos = pos;
4835 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4836 oScreenedLine.bSublinesSet = true;
4837 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4839 return CountMultiLines(nViewLine);
4842 /// prepare inline diff cache
4843 LineColors & CBaseView::GetLineColors(int nViewLine)
4845 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4847 if (m_bWhitespaceInlineDiffs)
4849 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4850 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4852 else
4854 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4855 return m_ScreenedViewLine[nViewLine].lineColors;
4858 LineColors oLineColors;
4859 // set main line color
4860 COLORREF crBkgnd, crText;
4861 DiffStates diffState = m_pViewData->GetState(nViewLine);
4862 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4863 oLineColors.SetColor(0, crText, crBkgnd);
4865 do {
4866 if (!m_bShowInlineDiff)
4867 break;
4869 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4870 break;
4872 CString sLine = GetViewLineChars(nViewLine);
4873 if (sLine.IsEmpty())
4874 break;
4875 if (!m_pOtherView)
4876 break;
4878 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4879 if (sDiffLine.IsEmpty())
4880 break;
4882 svn_diff_t * diff = NULL;
4883 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4884 break;
4885 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4886 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4887 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4888 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4889 break;
4891 int lineoffset = 0;
4892 int nTextStartOffset = 0;
4893 std::map<int, COLORREF> removedPositions;
4894 while (diff)
4896 if (this == m_pwndRight)
4898 apr_off_t nTmp = diff->modified_length;
4899 diff->modified_length = diff->original_length;
4900 diff->original_length = nTmp;
4902 nTmp = diff->modified_start;
4903 diff->modified_start = diff->original_start;
4904 diff->original_start = nTmp;
4906 apr_off_t len = diff->original_length;
4908 size_t nTextLength = 0;
4909 for (int i = 0; i < len; ++i)
4911 nTextLength += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4912 lineoffset++;
4914 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4916 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4917 if ((m_bShowInlineDiff)&&(bInlineDiff))
4919 crBkgnd = InlineViewLineDiffColor(nViewLine);
4921 else
4923 crBkgnd = m_ModifiedBk;
4926 if (len < diff->modified_length)
4928 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4930 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4932 nTextStartOffset += (int)nTextLength;
4933 diff = diff->next;
4935 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4937 oLineColors.AddShotColor(it->first, it->second);
4939 } while (false); // error catch
4941 if (!m_bWhitespaceInlineDiffs)
4943 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4944 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4946 else
4948 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4949 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4952 return GetLineColors(nViewLine);
4955 void CBaseView::OnEditSelectall()
4957 if (m_pViewData == nullptr)
4958 return;
4959 int nLastViewLine = m_pViewData->GetCount()-1;
4960 if (nLastViewLine < 0)
4961 return;
4962 SetupAllViewSelection(0, nLastViewLine);
4964 CString sLine = GetViewLineChars(nLastViewLine);
4965 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4966 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4967 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4969 UpdateWindow();
4972 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4974 FilterWhitespaces(first);
4975 FilterWhitespaces(second);
4978 void CBaseView::FilterWhitespaces(CString& line)
4980 line.Remove(' ');
4981 line.Remove('\t');
4982 line.Remove('\r');
4983 line.Remove('\n');
4986 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4988 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4989 int nEventLine = nLineFromTop + m_nTopLine;
4990 nEventLine--; //we need the index
4991 return nEventLine;
4995 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4997 if (RelayTrippleClick(pMsg))
4998 return TRUE;
4999 return CView::PreTranslateMessage(pMsg);
5003 void CBaseView::ResetUndoStep()
5005 m_AllState.Clear();
5008 void CBaseView::SaveUndoStep()
5010 if (!m_AllState.IsEmpty())
5012 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
5014 ResetUndoStep();
5017 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
5019 m_pState->addedlines.push_back(index);
5020 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
5023 void CBaseView::InsertViewData( int index, const viewdata& data )
5025 m_pState->addedlines.push_back(index);
5026 m_pViewData->InsertData(index, data);
5029 void CBaseView::RemoveViewData( int index )
5031 m_pState->removedlines[index] = m_pViewData->GetData(index);
5032 m_pViewData->RemoveData(index);
5035 void CBaseView::SetViewData( int index, const viewdata& data )
5037 m_pState->replacedlines[index] = m_pViewData->GetData(index);
5038 m_pViewData->SetData(index, data);
5041 void CBaseView::SetViewState( int index, DiffStates state )
5043 m_pState->linestates[index] = m_pViewData->GetState(index);
5044 m_pViewData->SetState(index, state);
5047 void CBaseView::SetViewLine( int index, const CString& sLine )
5049 m_pState->difflines[index] = m_pViewData->GetLine(index);
5050 m_pViewData->SetLine(index, sLine);
5053 void CBaseView::SetViewLineNumber( int index, int linenumber )
5055 int oldLineNumber = m_pViewData->GetLineNumber(index);
5056 if (oldLineNumber != linenumber) {
5057 m_pState->linelines[index] = oldLineNumber;
5058 m_pViewData->SetLineNumber(index, linenumber);
5062 void CBaseView::SetViewLineEnding( int index, EOL ending )
5064 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
5065 m_pViewData->SetLineEnding(index, ending);
5068 void CBaseView::SetViewMarked( int index, bool marked )
5070 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5071 m_pViewData->SetMarked(index, marked);
5075 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5077 if (HasSelection())
5079 start = m_nSelViewBlockStart;
5080 end = m_nSelViewBlockEnd;
5081 return true;
5083 return false;
5086 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5088 RebuildIfNecessary();
5089 if ((size() <= screenLine) || (screenLine < 0))
5090 return 0;
5091 return m_Screen2View[screenLine].nViewLine;
5094 int CBaseView::Screen2View::size()
5096 RebuildIfNecessary();
5097 return (int)m_Screen2View.size();
5100 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5102 RebuildIfNecessary();
5103 if (size() <= screenLine)
5104 return 0;
5105 return m_Screen2View[screenLine].nViewSubLine;
5108 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5110 RebuildIfNecessary();
5111 return m_Screen2View[screenLine];
5115 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5117 void CBaseView::Screen2View::RebuildIfNecessary()
5119 if (!m_pViewData)
5120 return; // rebuild not necessary
5122 FixScreenedCacheSize(m_pwndLeft);
5123 FixScreenedCacheSize(m_pwndRight);
5124 FixScreenedCacheSize(m_pwndBottom);
5125 if (!m_bFull)
5127 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5129 ResetScreenedViewLineCache(m_pwndLeft, *it);
5130 ResetScreenedViewLineCache(m_pwndRight, *it);
5131 ResetScreenedViewLineCache(m_pwndBottom, *it);
5134 else
5136 ResetScreenedViewLineCache(m_pwndLeft);
5137 ResetScreenedViewLineCache(m_pwndRight);
5138 ResetScreenedViewLineCache(m_pwndBottom);
5140 m_RebuildRanges.clear();
5141 m_bFull = false;
5143 size_t OldSize = m_Screen2View.size();
5144 m_Screen2View.clear();
5145 m_Screen2View.reserve(OldSize); // guess same size
5146 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5148 if (m_pMainFrame->m_bCollapsed)
5150 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5151 ++i;
5152 if (!(i < m_pViewData->GetCount()))
5153 break;
5155 TScreenLineInfo oLineInfo;
5156 oLineInfo.nViewLine = i;
5157 oLineInfo.nViewSubLine = -1; // no wrap
5158 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5160 int nMaxLines = 0;
5161 if (IsLeftViewGood())
5162 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5163 if (IsRightViewGood())
5164 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5165 if (IsBottomViewGood())
5166 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5167 for (int l = 0; l < (nMaxLines-1); ++l)
5169 oLineInfo.nViewSubLine++;
5170 m_Screen2View.push_back(oLineInfo);
5172 oLineInfo.nViewSubLine++;
5174 m_Screen2View.push_back(oLineInfo);
5176 m_pViewData = NULL;
5178 if (IsLeftViewGood())
5179 m_pwndLeft->BuildMarkedWordArray();
5180 if (IsRightViewGood())
5181 m_pwndRight->BuildMarkedWordArray();
5182 if (IsBottomViewGood())
5183 m_pwndBottom->BuildMarkedWordArray();
5184 UpdateLocator();
5185 RecalcAllVertScrollBars();
5186 RecalcAllHorzScrollBars();
5189 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5191 RebuildIfNecessary();
5193 int nScreenLineCount = (int)m_Screen2View.size();
5195 int nPos = 0;
5196 if (nScreenLineCount>16)
5198 // for enough long data search for last screen
5199 // with viewline less than one we are looking for
5200 // use approximate method (based on) binary search using asymmetric start point
5201 // in form 2**n (determined as MSB of length) to go around division and rounding;
5202 // this effectively looks for bit values from MSB to LSB
5204 int nTestBit;
5205 //GetMostSignificantBitValue
5206 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5207 nTestBit = nScreenLineCount;
5208 nTestBit |= nTestBit>>1;
5209 nTestBit |= nTestBit>>2;
5210 nTestBit |= nTestBit>>4;
5211 nTestBit |= nTestBit>>8;
5212 nTestBit |= nTestBit>>16;
5213 nTestBit ^= (nTestBit>>1);
5215 while (nTestBit)
5217 int nTestPos = nPos | nTestBit;
5218 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5220 nPos = nTestPos;
5222 nTestBit >>= 1;
5225 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5227 nPos++;
5230 return nPos;
5233 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5234 m_bFull = true;
5236 m_pViewData = pViewData;
5239 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5241 if (m_bFull)
5242 return;
5244 m_pViewData = pViewData;
5246 TRebuildRange Range;
5247 Range.FirstViewLine=nFirstViewLine;
5248 Range.LastViewLine=nLastViewLine;
5249 m_RebuildRanges.push_back(Range);
5252 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5254 if (!IsViewGood(pwndView))
5256 return false;
5258 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5259 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5260 if (nOldSize == nViewCount)
5262 return false;
5264 pwndView->m_ScreenedViewLine.resize(nViewCount);
5265 return true;
5268 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5270 if (!IsViewGood(pwndView))
5272 return false;
5274 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5275 ResetScreenedViewLineCache(pwndView, Range);
5276 return true;
5279 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5281 if (!IsViewGood(pwndView))
5283 return false;
5285 if (Range.LastViewLine == -1)
5287 return false;
5289 ASSERT(Range.FirstViewLine >= 0);
5290 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5291 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5293 pwndView->m_ScreenedViewLine[i].Clear();
5295 return false;
5298 void CBaseView::WrapChanged()
5300 m_nMaxLineLength = -1;
5301 m_nOffsetChar = 0;
5304 void CBaseView::OnEditFind()
5306 if (m_pFindDialog)
5307 return;
5309 int id = 0;
5310 if (this == m_pwndLeft)
5311 id = 1;
5312 if (this == m_pwndRight)
5313 id = 2;
5314 if (this == m_pwndBottom)
5315 id = 3;
5317 m_pFindDialog = new CFindDlg(this);
5318 m_pFindDialog->Create(this, id);
5320 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5321 m_pFindDialog->SetReadonly(m_bReadonly);
5324 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5326 ASSERT(m_pFindDialog != NULL);
5328 if (m_pFindDialog->IsTerminating())
5330 // invalidate the handle identifying the dialog box.
5331 m_pFindDialog = NULL;
5332 return 0;
5335 if(m_pFindDialog->FindNext())
5337 //read data from dialog
5338 m_sFindText = m_pFindDialog->GetFindString();
5339 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5340 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5341 m_bWholeWord = m_pFindDialog->WholeWord();
5343 if (!m_bMatchCase)
5344 m_sFindText = m_sFindText.MakeLower();
5346 BuildFindStringArray();
5347 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5349 if (m_pFindDialog->SearchUp())
5350 OnEditFindprev();
5351 else
5352 OnEditFindnext();
5354 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5356 size_t count = 0;
5357 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5358 count += m_arFindStringLines[i];
5359 CString format;
5360 format.LoadString(IDS_FIND_COUNT);
5361 CString matches;
5362 matches.Format(format, count);
5363 m_pFindDialog->SetStatusText(matches);
5365 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5367 if (!IsWritable())
5368 return 0;
5369 bool bFound = false;
5370 if (m_pFindDialog->SearchUp())
5371 bFound = Search(SearchPrevious, true, true, false);
5372 else
5373 bFound = Search(SearchNext, true, true, false);
5374 if (bFound)
5376 CString sReplaceText = m_pFindDialog->GetReplaceString();
5377 CUndo::GetInstance().BeginGrouping();
5378 RemoveSelectedText();
5379 InsertText(sReplaceText);
5380 CUndo::GetInstance().EndGrouping();
5384 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5386 if (!IsWritable())
5387 return 0;
5388 bool bFound = false;
5389 int replaceCount = 0;
5390 POINT lastPoint = m_ptSelectionViewPosStart;
5391 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5392 CUndo::GetInstance().BeginGrouping();
5395 bFound = Search(SearchNext, true, false, true);
5396 if (bFound)
5398 CString sReplaceText = m_pFindDialog->GetReplaceString();
5399 RemoveSelectedText();
5400 InsertText(sReplaceText);
5401 ++replaceCount;
5403 } while (bFound);
5404 CUndo::GetInstance().EndGrouping();
5405 if (replaceCount == 0)
5406 m_ptSelectionViewPosStart = lastPoint;
5407 CString message;
5408 message.Format(IDS_FIND_REPLACED, replaceCount);
5409 if (m_pFindDialog)
5410 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5415 return 0;
5418 void CBaseView::OnEditFindnextStart()
5420 if (m_pViewData == nullptr)
5421 return;
5422 if (HasTextSelection())
5424 m_sFindText = GetSelectedText();
5425 m_bMatchCase = false;
5426 m_bLimitToDiff = false;
5427 m_bWholeWord = false;
5428 m_sFindText = m_sFindText.MakeLower();
5430 BuildFindStringArray();
5431 OnEditFindnext();
5433 else
5435 m_sFindText.Empty();
5436 BuildFindStringArray();
5440 void CBaseView::OnEditFindprevStart()
5442 if (m_pViewData == nullptr)
5443 return;
5444 if (HasTextSelection())
5446 m_sFindText = GetSelectedText();
5447 m_bMatchCase = false;
5448 m_bLimitToDiff = false;
5449 m_bWholeWord = false;
5450 m_sFindText = m_sFindText.MakeLower();
5452 BuildFindStringArray();
5453 OnEditFindprev();
5455 else
5457 m_sFindText.Empty();
5458 BuildFindStringArray();
5462 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5464 if (srchDir == SearchPrevious)
5466 int laststart = -1;
5467 int laststart2 = -1;
5470 laststart2 = laststart;
5471 laststart = str.Find(m_sFindText, laststart + 1);
5472 } while (laststart >= 0 && laststart < start);
5473 start = laststart2;
5475 else
5476 start = str.Find(m_sFindText, start);
5477 end = start + m_sFindText.GetLength();
5478 bool bStringFound = (start >= 0);
5479 if (bStringFound && m_bWholeWord)
5481 if (start)
5482 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5484 if (bStringFound)
5486 if (str.GetLength() > end)
5487 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5490 return bStringFound;
5493 void CBaseView::OnEditFindprev()
5495 Search(SearchPrevious, false, true, false);
5498 void CBaseView::OnEditFindnext()
5500 Search(SearchNext, false, true, false);
5503 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5505 if (m_sFindText.IsEmpty())
5506 return false;
5507 if(!m_pViewData)
5508 return false;
5510 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5511 POINT end;
5512 end.y = m_pViewData->GetCount()-1;
5513 if (end.y < 0)
5514 return false;
5516 if (srchDir==SearchNext)
5517 end.x = GetViewLineLength(end.y);
5518 else
5520 end.x = m_ptSelectionViewPosStart.x;
5521 start.x = 0;
5524 if (!HasTextSelection())
5526 start.y = m_ptCaretViewPos.y;
5527 if (srchDir==SearchNext)
5528 start.x = m_ptCaretViewPos.x;
5529 else
5531 start.x = 0;
5532 end.x = m_ptCaretViewPos.x;
5535 CString sSelectedText;
5536 int startline = -1;
5537 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5539 if (nViewLine < 0)
5541 if (stopEof)
5542 return false;
5543 nViewLine = m_pViewData->GetCount()-1;
5544 startline = start.y;
5545 if (flashIfNotFound)
5547 if (m_pFindDialog)
5548 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5549 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5552 if (nViewLine > end.y)
5554 if (stopEof)
5555 return false;
5556 nViewLine = 0;
5557 startline = start.y;
5558 if (flashIfNotFound)
5560 if (m_pFindDialog)
5561 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5562 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5565 switch (m_pViewData->GetState(nViewLine))
5567 case DIFFSTATE_EMPTY:
5568 break;
5569 case DIFFSTATE_UNKNOWN:
5570 case DIFFSTATE_NORMAL:
5571 if (m_bLimitToDiff)
5572 break;
5573 case DIFFSTATE_REMOVED:
5574 case DIFFSTATE_REMOVEDWHITESPACE:
5575 case DIFFSTATE_ADDED:
5576 case DIFFSTATE_ADDEDWHITESPACE:
5577 case DIFFSTATE_WHITESPACE:
5578 case DIFFSTATE_WHITESPACE_DIFF:
5579 case DIFFSTATE_CONFLICTED:
5580 case DIFFSTATE_CONFLICTED_IGNORED:
5581 case DIFFSTATE_CONFLICTADDED:
5582 case DIFFSTATE_CONFLICTEMPTY:
5583 case DIFFSTATE_CONFLICTRESOLVED:
5584 case DIFFSTATE_IDENTICALREMOVED:
5585 case DIFFSTATE_IDENTICALADDED:
5586 case DIFFSTATE_THEIRSREMOVED:
5587 case DIFFSTATE_THEIRSADDED:
5588 case DIFFSTATE_YOURSREMOVED:
5589 case DIFFSTATE_YOURSADDED:
5590 case DIFFSTATE_EDITED:
5592 sSelectedText = GetViewLineChars(nViewLine);
5593 if (nViewLine == start.y && startline < 0)
5594 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5595 if (!m_bMatchCase)
5596 sSelectedText = sSelectedText.MakeLower();
5597 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5598 int endfound = 0;
5599 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5601 HighlightViewLines(nViewLine, nViewLine);
5602 m_ptSelectionViewPosStart.x = startfound;
5603 m_ptSelectionViewPosEnd.x = endfound;
5604 if (nViewLine == start.y && startline < 0)
5606 m_ptSelectionViewPosStart.x += start.x;
5607 m_ptSelectionViewPosEnd.x += start.x;
5609 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5610 m_ptSelectionViewPosStart.y = nViewLine;
5611 m_ptSelectionViewPosEnd.y = nViewLine;
5612 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5613 UpdateViewsCaretPosition();
5614 EnsureCaretVisible();
5615 Invalidate();
5616 return true;
5619 break;
5622 if (startline >= 0)
5624 if (nViewLine == startline)
5626 if (flashIfNotFound)
5628 CString message;
5629 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5630 if (m_pFindDialog)
5631 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5632 ::MessageBeep(0xFFFFFFFF);
5633 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5635 break;
5639 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5640 return false;
5643 CString CBaseView::GetSelectedText() const
5645 CString sSelectedText;
5646 POINT start = m_ptSelectionViewPosStart;
5647 POINT end = m_ptSelectionViewPosEnd;
5648 if (!HasTextSelection())
5650 if (!HasSelection())
5651 return sSelectedText;
5652 start.y = m_nSelViewBlockStart;
5653 start.x = 0;
5654 end.y = m_nSelViewBlockEnd;
5655 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5657 if (m_pViewData == nullptr)
5658 return sSelectedText;
5659 // first store the selected lines in one CString
5660 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5662 switch (m_pViewData->GetState(nViewLine))
5664 case DIFFSTATE_EMPTY:
5665 break;
5666 case DIFFSTATE_UNKNOWN:
5667 case DIFFSTATE_NORMAL:
5668 case DIFFSTATE_REMOVED:
5669 case DIFFSTATE_REMOVEDWHITESPACE:
5670 case DIFFSTATE_ADDED:
5671 case DIFFSTATE_ADDEDWHITESPACE:
5672 case DIFFSTATE_WHITESPACE:
5673 case DIFFSTATE_WHITESPACE_DIFF:
5674 case DIFFSTATE_CONFLICTED:
5675 case DIFFSTATE_CONFLICTED_IGNORED:
5676 case DIFFSTATE_CONFLICTADDED:
5677 case DIFFSTATE_CONFLICTEMPTY:
5678 case DIFFSTATE_CONFLICTRESOLVED:
5679 case DIFFSTATE_IDENTICALREMOVED:
5680 case DIFFSTATE_IDENTICALADDED:
5681 case DIFFSTATE_THEIRSREMOVED:
5682 case DIFFSTATE_THEIRSADDED:
5683 case DIFFSTATE_YOURSREMOVED:
5684 case DIFFSTATE_YOURSADDED:
5685 case DIFFSTATE_EDITED:
5686 sSelectedText += GetViewLineChars(nViewLine);
5687 sSelectedText += _T("\r\n");
5688 break;
5691 // remove the non-selected chars from the first line, last line and last \r\n
5692 int nLeftCut = start.x;
5693 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5694 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5695 return sSelectedText;
5698 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5700 hasMods = false;
5701 hasConflicts = false;
5702 hasWhitespaceMods = false;
5704 if (m_pViewData)
5706 for (int i=0; i<m_pViewData->GetCount(); i++)
5708 DiffStates state = m_pViewData->GetState(i);
5709 switch (state)
5711 case DIFFSTATE_ADDED:
5712 case DIFFSTATE_IDENTICALADDED:
5713 case DIFFSTATE_THEIRSADDED:
5714 case DIFFSTATE_YOURSADDED:
5715 case DIFFSTATE_CONFLICTADDED:
5716 case DIFFSTATE_IDENTICALREMOVED:
5717 case DIFFSTATE_REMOVED:
5718 case DIFFSTATE_THEIRSREMOVED:
5719 case DIFFSTATE_YOURSREMOVED:
5720 case DIFFSTATE_EMPTY:
5721 hasMods = true;
5722 break;
5723 case DIFFSTATE_CONFLICTED:
5724 case DIFFSTATE_CONFLICTED_IGNORED:
5725 hasConflicts = true;
5726 break;
5727 case DIFFSTATE_REMOVEDWHITESPACE:
5728 case DIFFSTATE_ADDEDWHITESPACE:
5729 case DIFFSTATE_WHITESPACE:
5730 case DIFFSTATE_WHITESPACE_DIFF:
5731 hasWhitespaceMods = true;
5732 break;
5738 void CBaseView::OnEditGotoline()
5740 if (m_pViewData == NULL)
5741 return;
5742 // find the last and first line number
5743 int nViewLineCount = m_pViewData->GetCount();
5745 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5746 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5748 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5749 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5751 break;
5754 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5756 return;
5758 nLastLineNumber++;
5759 int nFirstLineNumber=1; // first is always 1
5761 CString sText;
5762 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5764 CGotoLineDlg dlg(this);
5765 dlg.SetLabel(sText);
5766 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5767 if (dlg.DoModal() == IDOK)
5769 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5771 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5773 HighlightViewLines(nViewLine, nViewLine);
5774 return;
5780 void CBaseView::OnToggleReadonly()
5782 if (IsReadonlyChangable()) {
5783 SetWritable(IsReadonly());
5787 int CBaseView::SaveFile(int nFlags)
5789 Invalidate();
5790 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5792 CFileTextLines file;
5793 m_SaveParams.m_LineEndings = m_lineendings;
5794 m_SaveParams.m_UnicodeType = m_texttype;
5795 file.SetSaveParams(m_SaveParams);
5797 for (int i=0; i<m_pViewData->GetCount(); i++)
5799 //only copy non-removed lines
5800 DiffStates state = m_pViewData->GetState(i);
5801 switch (state)
5803 case DIFFSTATE_CONFLICTED:
5804 case DIFFSTATE_CONFLICTED_IGNORED:
5806 int first = i;
5807 int last = i;
5810 last++;
5811 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5812 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5813 for (int j=first; j<last; j++)
5815 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5817 file.Add(_T("======="), EOL_NOENDING);
5818 for (int j=first; j<last; j++)
5820 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5822 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5823 i = last-1;
5825 break;
5826 case DIFFSTATE_EMPTY:
5827 break;
5828 case DIFFSTATE_CONFLICTEMPTY:
5829 case DIFFSTATE_IDENTICALREMOVED:
5830 case DIFFSTATE_REMOVED:
5831 case DIFFSTATE_THEIRSREMOVED:
5832 case DIFFSTATE_YOURSREMOVED:
5833 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5834 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5836 // do not save removed lines
5837 break;
5839 default:
5840 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5841 break;
5844 CString filename = m_pWorkingFile->GetFilename();
5845 if (m_pWorkingFile->IsReadonly())
5846 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5847 return -1;
5848 if (!file.Save(filename))
5850 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5851 return -1;
5853 m_pWorkingFile->SetFileName(filename);
5854 m_pWorkingFile->StoreFileAttributes();
5855 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5856 SetModified(FALSE);
5857 CUndo::GetInstance().MarkAsOriginalState(
5858 this == m_pwndLeft,
5859 this == m_pwndRight,
5860 this == m_pwndBottom);
5861 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5862 return 0;
5863 return file.GetCount();
5865 return 1;
5869 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5871 if (m_pWorkingFile)
5873 m_pWorkingFile->SetFileName(sFileName);
5874 return SaveFile(nFlags);
5876 return -1;
5880 EOL CBaseView::GetLineEndings()
5882 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5885 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5887 if (bHasMixedEols)
5889 return EOL_AUTOLINE; // mixed eols - hack value
5891 if (m_lineendings == EOL_AUTOLINE)
5893 return EOL_CRLF;
5895 return m_lineendings;
5898 void CBaseView::ReplaceLineEndings(EOL eEol)
5900 if (eEol == EOL_AUTOLINE)
5902 return;
5904 // set AUTOLINE
5905 m_lineendings = eEol;
5906 // replace all set EOLs
5907 // TODO store line endings and lineendings in undo
5908 //CUndo::BeginGrouping();
5909 for (int i = 0; i < GetViewCount(); ++i)
5911 if (IsLineEmpty(i))
5913 continue;
5915 EOL eLineEol = GetViewLineEnding(i);
5916 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5918 continue;
5920 SetViewLineEnding(i, eEol);
5922 //CUndo::EndGrouping();
5923 //CUndo::saveundostep;
5924 DocumentUpdated();
5925 SetModified();
5928 void CBaseView::SetLineEndingStyle(EOL eEol)
5930 m_lineendings = eEol;
5933 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5935 if (m_texttype == eTextType)
5937 return;
5939 m_texttype = eTextType;
5940 DocumentUpdated();
5941 SetModified();
5944 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5946 if (IsReadonly())
5947 return; // nothing to be changed in read-only view
5948 CEncodingDlg dlg;
5949 dlg.view.LoadString(nTextId);
5950 dlg.texttype = m_texttype;
5951 dlg.lineendings = GetLineEndings();
5952 if (dlg.DoModal() != IDOK)
5953 return;
5954 SetTextType(dlg.texttype);
5955 ReplaceLineEndings(dlg.lineendings);
5959 Replaces lines from source view to this
5961 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
5963 if (!IsViewGood(pwndView))
5964 return;
5965 if (!IsWritable())
5966 return;
5967 CUndo::GetInstance().BeginGrouping();
5969 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5971 bool skip = fnSkip(viewLine);
5972 if (skip)
5974 if (GetViewMarked(viewLine))
5975 SetViewMarked(viewLine, false);
5976 continue;
5978 viewdata line = pwndView->GetViewData(viewLine);
5979 if (line.ending != EOL_NOENDING)
5980 line.ending = m_lineendings;
5981 switch (line.state)
5983 case DIFFSTATE_CONFLICTEMPTY:
5984 case DIFFSTATE_UNKNOWN:
5985 line.state = DIFFSTATE_EMPTY;
5986 case DIFFSTATE_EMPTY:
5987 break;
5988 case DIFFSTATE_ADDED:
5989 case DIFFSTATE_CONFLICTADDED:
5990 case DIFFSTATE_CONFLICTED:
5991 case DIFFSTATE_CONFLICTED_IGNORED:
5992 case DIFFSTATE_IDENTICALADDED:
5993 case DIFFSTATE_THEIRSADDED:
5994 case DIFFSTATE_YOURSADDED:
5995 case DIFFSTATE_IDENTICALREMOVED:
5996 case DIFFSTATE_REMOVED:
5997 case DIFFSTATE_THEIRSREMOVED:
5998 case DIFFSTATE_YOURSREMOVED:
5999 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
6000 line.state = DIFFSTATE_NORMAL;
6001 case DIFFSTATE_NORMAL:
6002 break;
6003 default:
6004 break;
6006 bool marked = GetViewMarked(viewLine);
6007 SetViewData(viewLine, line);
6008 if (marked)
6009 SetViewMarked(viewLine, false);
6010 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
6012 // if this view is in ASCII and the other is not, we have to make sure that
6013 // the text we copy from the other view can actually be saved in ASCII encoding.
6014 // if not, we have to change this views encoding to the same encoding as the other view
6015 BOOL useDefault = FALSE;
6016 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
6017 if (useDefault) // a default char is required, so the char can not be saved as ASCII
6018 SetTextType(pwndView->GetTextType());
6021 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6022 // TODO: check if copied line is same as original one set modified only when differ
6023 SetModified();
6024 SaveUndoStep();
6026 int nRemovedLines = CleanEmptyLines();
6027 SaveUndoStep();
6028 //VerifyEols();
6029 // make sure all non empty line have EOL set but last
6030 // wrong can be last copied line(have eol, but no line under),
6031 // or old last line (line before copied block missing eol, but have line under)
6032 // we'll check all lines to be sure
6033 int nLine = GetViewCount();
6034 // check last line have no EOL set
6035 while (--nLine>=0)
6037 if (!IsViewLineEmpty(nLine))
6039 if (GetViewLineEnding(nLine) != EOL_NOENDING)
6041 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6042 // so next line is empty
6043 ASSERT(IsViewLineEmpty(nLine+1));
6044 // and we can turn it to normal empty line
6045 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
6047 break;
6050 // check all (nonlast) line have EOL set
6051 while (--nLine>=0)
6053 if (!IsViewLineEmpty(nLine))
6055 if (GetViewLineEnding(nLine) == EOL_NOENDING)
6057 SetViewLineEnding(nLine, m_lineendings);
6058 // in theory there should be only one line needing fix, but most of time we get over all anyway
6059 // break;
6063 SaveUndoStep();
6064 UpdateViewLineNumbers();
6065 SaveUndoStep();
6067 CUndo::GetInstance().EndGrouping();
6069 if (nRemovedLines!=0)
6071 // some lines are gone update selection
6072 ClearSelection();
6073 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6075 BuildAllScreen2ViewVector();
6076 pwndView->Invalidate();
6077 RefreshViews();
6080 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6082 if (!IsWritable())
6083 return;
6084 CUndo::GetInstance().BeginGrouping();
6086 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6087 SetViewMarked(viewLine, marked);
6089 SetModified();
6090 SaveUndoStep();
6091 CUndo::GetInstance().EndGrouping();
6093 BuildAllScreen2ViewVector();
6094 Invalidate();
6095 RefreshViews();
6098 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6100 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6101 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6104 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6106 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6107 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6110 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6112 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6113 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6116 int CBaseView::GetIndentCharsForLine(int x, int y)
6118 const int maxGuessLine = 100;
6119 int nTabMode = -1;
6120 CString line = GetViewLine(y);
6121 if (m_nTabMode & TABMODE_SMARTINDENT)
6123 // detect left char and right char
6124 TCHAR lc = x > 0 ? line[x - 1] : '\0';
6125 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
6126 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
6127 nTabMode = 1;
6128 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
6129 nTabMode = 0;
6130 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
6131 nTabMode = m_nTabMode & TABMODE_USESPACES;
6133 // detect lines nearby
6134 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6136 bool above = i > 0 && i >= y - maxGuessLine;
6137 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6138 if (!(above || below))
6139 break;
6140 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
6141 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
6142 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
6143 nTabMode = 1;
6144 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
6145 nTabMode = 0;
6146 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
6147 nTabMode = m_nTabMode & TABMODE_USESPACES;
6150 else
6151 nTabMode = m_nTabMode & TABMODE_USESPACES;
6153 if (nTabMode > 0)
6155 // use spaces
6156 x = CountExpandedChars(line, x);
6157 return (m_nTabSize - (x % m_nTabSize));
6160 // use tab
6161 return 0;
6164 void CBaseView::AddIndentationForSelectedBlock()
6166 bool bModified = false;
6167 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6169 // skip the line if no character is selected in the last selected line
6170 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6172 continue;
6174 // skip empty lines
6175 if (IsLineEmpty(nViewLine))
6177 continue;
6179 const CString &sLine = GetViewLine(nViewLine);
6180 CString sTemp = sLine;
6181 if (sTemp.Trim().IsEmpty())
6183 // skip empty and whitechar only lines
6184 continue;
6186 // add tab to line start (alternatively m_nTabSize spaces can be used)
6187 CString tabStr;
6188 int indentChars = GetIndentCharsForLine(0, nViewLine);
6189 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
6190 SetViewLine(nViewLine, tabStr + sLine);
6191 bModified = true;
6193 if (bModified)
6195 SetModified();
6196 SaveUndoStep();
6197 BuildAllScreen2ViewVector();
6201 void CBaseView::RemoveIndentationForSelectedBlock()
6203 bool bModified = false;
6204 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6206 // skip the line if no character is selected in the last selected line
6207 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6209 continue;
6211 // skip empty lines
6212 if (IsLineEmpty(nViewLine))
6214 continue;
6216 CString sLine = GetViewLine(nViewLine);
6217 // remove up to n spaces from line start
6218 // and one tab (if less then n spaces was removed)
6219 int nPos = 0;
6220 while (nPos<m_nTabSize)
6222 switch (sLine[nPos])
6224 case ' ':
6225 nPos++;
6226 continue;
6227 case '\t':
6228 nPos++;
6230 break;
6232 if (nPos>0)
6234 sLine.Delete(0, nPos);
6235 SetViewLine(nViewLine, sLine);
6236 bModified = true;
6239 if (bModified)
6241 SetModified();
6242 SaveUndoStep();
6243 BuildAllScreen2ViewVector();
6248 there are two possible versions
6249 - convert tabs to spaces only in front of text (implemented)
6250 - convert all tabs to spaces
6252 void CBaseView::ConvertTabToSpaces()
6254 bool bModified = false;
6255 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6257 if (IsLineEmpty(nViewLine))
6259 continue;
6261 const CString &sLine = GetViewLine(nViewLine);
6262 bool bTabToConvertFound = false;
6263 int nPosIn = 0;
6264 int nPosOut = 0;
6265 while (nPosIn<sLine.GetLength())
6267 switch (sLine[nPosIn])
6269 case ' ':
6270 nPosIn++;
6271 nPosOut++;
6272 continue;
6273 case '\t':
6274 nPosIn++;
6275 bTabToConvertFound = true;
6276 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6277 continue;
6279 break;
6281 if (bTabToConvertFound)
6283 CString sLineNew = sLine;
6284 sLineNew.Delete(0, nPosIn);
6285 sLineNew = CString(' ', nPosOut) + sLineNew;
6286 SetViewLine(nViewLine, sLineNew);
6287 bModified = true;
6290 if (bModified)
6292 SetModified();
6293 SaveUndoStep();
6294 BuildAllScreen2ViewVector();
6299 there are two possible version
6300 - convert spaces to tabs only in front of text (implemented)
6301 - convert all spaces to tabs
6303 void CBaseView::Tabularize()
6305 bool bModified = false;
6306 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6308 if (IsLineEmpty(nViewLine))
6310 continue;
6312 const CString &sLine = GetViewLine(nViewLine);
6313 int nDel = 0;
6314 int nTabCount = 0; // total tabs to be used
6315 int nSpaceCount = 0; // number of spaces in tab size run
6316 int nPos = 0;
6317 while (nPos<sLine.GetLength())
6319 switch (sLine[nPos++])
6321 case ' ':
6322 //bSpace = true;
6323 if (++nSpaceCount < m_nTabSize)
6325 continue;
6327 case '\t':
6328 nTabCount++;
6329 nSpaceCount = 0;
6330 nDel = nPos;
6331 continue;
6333 break;
6335 if (nDel > 0)
6337 CString sLineNew = sLine;
6338 sLineNew.Delete(0, nDel);
6339 sLineNew = CString('\t', nTabCount) + sLineNew;
6340 if (sLine!=sLineNew)
6342 SetViewLine(nViewLine, sLineNew);
6343 bModified = true;
6347 if (bModified)
6349 SetModified();
6350 SaveUndoStep();
6351 BuildAllScreen2ViewVector();
6355 void CBaseView::RemoveTrailWhiteChars()
6357 bool bModified = false;
6358 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6360 if (IsLineEmpty(nViewLine))
6362 continue;
6364 const CString &sLine = GetViewLine(nViewLine);
6365 CString sLineNew = sLine;
6366 sLineNew.TrimRight();
6367 if (sLine.GetLength()!=sLineNew.GetLength())
6369 SetViewLine(nViewLine, sLineNew);
6370 bModified = true;
6373 if (bModified)
6375 SetModified();
6376 SaveUndoStep();
6377 BuildAllScreen2ViewVector();
6381 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6383 if (GetViewCount()>10000)
6385 // 10k lines is enough to check
6386 TWhitecharsProperties oRet = {true, true, true, true};
6387 return oRet;
6389 TWhitecharsProperties oRet = {};
6390 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6392 if (IsLineEmpty(nViewLine))
6394 continue;
6396 const CString &sLine = GetViewLine(nViewLine);
6397 if (sLine.IsEmpty())
6399 continue;
6401 // check leading whites for convertible tabs and spaces
6402 int nPos = 0;
6403 int nSpaceCount = 0; // number of spaces in tab size run
6404 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6406 switch (sLine[nPos++])
6408 case ' ':
6409 if (++nSpaceCount >= m_nTabSize)
6411 oRet.HasSpacesToConvert = true;
6413 continue;
6414 case '\t':
6415 oRet.HasTabsToConvert = true;
6416 if (nSpaceCount!=0)
6418 oRet.HasSpacesToConvert = true;
6420 continue;
6422 break;
6425 // check trailing whites for removable chars
6426 switch (sLine[sLine.GetLength()-1])
6428 case ' ':
6429 case '\t':
6430 oRet.HasTrailWhiteChars = true;
6433 // check EOLs
6434 EOL eLineEol = GetViewLineEnding(nViewLine);
6435 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6437 oRet.HasMixedEols = true;
6440 return oRet;
6443 void CBaseView::InsertText(const CString& sText)
6445 ResetUndoStep();
6447 POINT ptCaretViewPos = GetCaretViewPosition();
6448 int nLeft = ptCaretViewPos.x;
6449 int nViewLine = ptCaretViewPos.y;
6451 if ((nViewLine == 0) && (GetViewCount() == 0))
6452 OnChar(VK_RETURN, 0, 0);
6454 std::vector<CString> lines;
6455 int nStart = 0;
6456 int nEolPos = 0;
6457 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6459 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6460 lines.push_back(sLine);
6461 nEolPos++;
6462 nStart = nEolPos;
6464 CString sLine = sText.Mid(nStart);
6465 lines.push_back(sLine);
6467 int nLinesToPaste = (int)lines.size();
6468 if (nLinesToPaste > 1)
6470 // multiline text
6472 // We want to undo the multiline insertion in a single step.
6473 CUndo::GetInstance().BeginGrouping();
6475 sLine = GetViewLineChars(nViewLine);
6476 CString sLineLeft = sLine.Left(nLeft);
6477 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6478 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6479 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6480 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6482 newLine.sLine = sLineLeft + lines[0];
6483 SetViewData(nViewLine, newLine);
6486 int nInsertLine = nViewLine;
6487 for (int i = 1; i < nLinesToPaste - 1; i++)
6489 newLine.sLine = lines[i];
6490 InsertViewData(++nInsertLine, newLine);
6492 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6493 newLine.ending = eOriginalEnding;
6494 InsertViewData(++nInsertLine, newLine);
6496 SetModified();
6497 SaveUndoStep();
6499 // adds new lines everywhere except me
6500 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6502 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6504 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6506 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6508 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6510 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6512 SaveUndoStep();
6514 UpdateViewLineNumbers();
6515 CUndo::GetInstance().EndGrouping();
6517 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6519 else
6521 // single line text - just insert it
6522 sLine = GetViewLineChars(nViewLine);
6523 sLine.Insert(nLeft, sText);
6524 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6525 SetViewLine(nViewLine, sLine);
6526 SetViewState(nViewLine, DIFFSTATE_EDITED);
6527 SetModified();
6528 SaveUndoStep();
6531 RefreshViews();
6532 BuildAllScreen2ViewVector();
6533 UpdateCaretViewPosition(ptCaretViewPos);