No need to check the pointer, was checked already above
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blobf60616555e152f4808a7c43137def08ee536a7ad
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(L"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
118 m_bViewLinenumbers = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
119 m_bShowInlineDiff = CRegDWORD(L"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE);
120 m_nInlineDiffMaxLineLength = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
121 m_InlineAddedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR);
122 m_InlineRemovedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR);
123 m_ModifiedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR);
124 m_WhiteSpaceFg = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW));
125 m_sWordSeparators = CRegString(L"Software\\TortoiseGitMerge\\WordSeparators", L"[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?");
126 m_bIconLFs = CRegDWORD(L"Software\\TortoiseGitMerge\\IconLFs", 0);
127 m_nTabSize = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabSize", 4);
128 m_nTabMode = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE);
129 m_bEditorConfigEnabled = !!(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE);
130 std::fill_n(m_apFonts, fontsCount, (CFont*)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 += L"??";
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(L"Software\\TortoiseGitMerge\\TabSize", 4);
252 m_nTabMode = (int)(DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE);
253 m_bViewLinenumbers = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
254 m_InlineAddedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR);
255 m_InlineRemovedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR);
256 m_ModifiedBk = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR);
257 m_WhiteSpaceFg = CRegDWORD(L"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW));
258 m_bIconLFs = CRegDWORD(L"Software\\TortoiseGitMerge\\IconLFs", 0);
259 m_nInlineDiffMaxLineLength = CRegDWORD(L"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(L"Software\\TortoiseGitMerge\\TabSize", 4);
274 m_nTabMode = (int)(DWORD)CRegDWORD(L"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 += L" / ";
353 if (nRemovedLines)
355 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
356 if (!sBarText.IsEmpty())
357 sBarText += L" / ";
358 sBarText += sTemp;
360 if (nAddedLines)
362 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
363 if (!sBarText.IsEmpty())
364 sBarText += L" / ";
365 sBarText += sTemp;
367 if (nConflictedLines)
369 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
370 if (!sBarText.IsEmpty())
371 sBarText += L" / ";
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(L"Software\\TortoiseGitMerge\\LogFontSize", 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
501 ReleaseDC(pDC);
503 wcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(L"Software\\TortoiseGitMerge\\LogFontName", L"Consolas"), _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(L"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(L"%%%ds", m_nDigits);
1358 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? L"↕⁞" : L"⁞"); // alternative …
1360 else if (nLineNumber >= 0)
1362 sLinenumberFormat.Format(L"%%%dd", m_nDigits);
1363 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1365 else if (m_pMainFrame->m_bWrapLines)
1367 sLinenumberFormat.Format(L"%%%ds", m_nDigits);
1368 sLinenumber.Format(sLinenumberFormat, L"·");
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(L"%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 = L"* " + 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 auto& second = it->second;
1791 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, second.background);
1792 if (second.shot == second.background)
1793 second.shot = crBk;
1794 second.background = crBk;
1795 second.text = CAppUtils::IntenseColor(intenseColorScale, second.text);
1800 // TODO: remove duplicate from selection and mark
1801 if (!m_sMarkedWord.IsEmpty())
1803 int nMarkLength = m_sMarkedWord.GetLength();
1804 //int nViewLineLength = sViewLine.GetLength();
1805 const TCHAR * text = sViewLine;
1806 const TCHAR * findText = text;
1807 while ((findText = wcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1809 int nMarkStart = static_cast<int>(findText - text);
1810 int nMarkEnd = nMarkStart + nMarkLength;
1811 findText += nMarkLength;
1812 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1813 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1814 if (eLeft == eStart)
1815 continue;
1816 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1817 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1818 if (eRight == eEnd)
1819 continue;
1821 // First enforce start and end point
1822 lineCols.SplitBlock(nMarkStart);
1823 lineCols.SplitBlock(nMarkEnd);
1824 // change color of affected parts
1825 const long int nIntenseColorScale = 200;
1826 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1827 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1829 auto& second = it->second;
1830 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1831 if (second.shot == second.background)
1832 second.shot = crBk;
1833 second.background = crBk;
1834 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1838 if (!m_sFindText.IsEmpty())
1840 int nMarkStart = 0;
1841 int nMarkEnd = 0;
1842 int nStringPos = nMarkStart;
1843 CString searchLine = sViewLine;
1844 if (!m_bMatchCase)
1845 searchLine.MakeLower();
1846 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1848 // First enforce start and end point
1849 lineCols.SplitBlock(nMarkStart+nStringPos);
1850 lineCols.SplitBlock(nMarkEnd+nStringPos);
1851 // change color of affected parts
1852 const long int nIntenseColorScale = 30;
1853 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1854 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1856 auto& second = it->second;
1857 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, second.background);
1858 if (second.shot == second.background)
1859 second.shot = crBk;
1860 second.background = crBk;
1861 second.text = CAppUtils::IntenseColor(nIntenseColorScale, second.text);
1863 searchLine = searchLine.Mid(nMarkEnd);
1864 nStringPos = nMarkEnd;
1865 nMarkStart = 0;
1866 nMarkEnd = 0;
1870 // @ this point we may cache data for next line which may be same in wrapped mode
1872 int nTextOffset = 0;
1873 int nSubline = GetSubLineOffset(nLineIndex);
1874 for (int n=0; n<nSubline; n++)
1876 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1877 nTextOffset += sLine.GetLength();
1880 CString sLine = GetLineChars(nLineIndex);
1881 int nLineLength = sLine.GetLength();
1882 CString sLineExp = ExpandChars(sLine);
1883 LPCTSTR textExp = sLineExp;
1884 //int nLineLengthExp = sLineExp.GetLength();
1885 int nStartExp = 0;
1886 int nLeft = coords.x;
1887 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1889 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1890 ++itEnd;
1891 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1892 int nEnd = nLineLength;
1893 if (itEnd != lineCols.end())
1895 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1897 int nBlockLength = nEnd - nStart;
1898 if (nBlockLength > 0 && nEnd>=0)
1900 auto& second = itStart->second;
1901 pDC->SetBkColor(second.background);
1902 pDC->SetTextColor(second.text);
1903 int nEndExp = CountExpandedChars(sLine, nEnd);
1904 int nTextLength = nEndExp - nStartExp;
1905 LPCTSTR p_zBlockText = textExp + nStartExp;
1906 SIZE Size;
1907 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1908 int nRight = nLeft + Size.cx;
1909 if ((nRight > rc.left) && (nLeft < rc.right))
1911 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1912 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1913 // is 4094 (4095 doesn't work anymore).
1914 // So we limit the length here to that 4094 chars.
1915 // In case we're scrolled to the right, there's no need to draw the string
1916 // from way outside our window, so we also offset the drawing to the start of the window.
1917 // This reduces the string length as well.
1918 int offset = 0;
1919 int leftcoord = nLeft;
1920 if (nLeft < 0)
1922 int fit = nTextLength;
1923 auto posBuffer = std::make_unique<int[]>(fit);
1924 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1925 int lower = 0, upper = fit - 1;
1928 int middle = (upper + lower + 1) / 2;
1929 int width = posBuffer.get()[middle];
1930 if (rc.left - nLeft < width)
1931 upper = middle - 1;
1932 else
1933 lower = middle;
1934 } while (lower < upper);
1936 offset = lower;
1937 nTextLength -= offset;
1938 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1941 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1942 if ((second.shot != second.background) && (itStart->first == nStart + nTextOffset))
1943 pDC->FillSolidRect(nLeft - 1, rc.top, 1, rc.Height(), second.shot);
1945 nLeft = nRight;
1946 coords.x = nRight;
1947 nStartExp = nEndExp;
1952 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1954 if (nLineIndex >= GetLineCount())
1955 nLineIndex = -1;
1956 ASSERT(nLineIndex >= -1);
1958 if ((nLineIndex == -1) || !m_pViewData)
1960 // Draw line beyond the text
1961 COLORREF crBkgnd, crText;
1962 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1963 pDC->FillSolidRect(rc, crBkgnd);
1964 return;
1967 int viewLine = GetViewLineForScreen(nLineIndex);
1968 if (m_pMainFrame->m_bCollapsed)
1970 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1972 COLORREF crBkgnd, crText;
1973 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1974 pDC->FillSolidRect(rc, crBkgnd);
1976 const int THICKNESS = 2;
1977 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1978 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1979 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1980 pDC->SetBkColor(crBkgnd);
1981 CRect rect = rc;
1982 pDC->DrawText(L"{...}", &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1983 return;
1987 DiffStates diffState = m_pViewData->GetState(viewLine);
1988 COLORREF crBkgnd, crText;
1989 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1991 if (diffState == DIFFSTATE_CONFLICTED)
1993 // conflicted lines are shown without 'text' on them
1994 CRect rect = rc;
1995 pDC->FillSolidRect(rc, crBkgnd);
1996 // now draw some faint text patterns
1997 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1998 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1999 DrawBlockLine(pDC, rc, nLineIndex);
2000 return;
2003 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
2004 CString sLine = GetLineChars(nLineIndex);
2005 if (sLine.IsEmpty())
2007 pDC->FillSolidRect(rc, crBkgnd);
2008 DrawBlockLine(pDC, rc, nLineIndex);
2009 DrawLineEnding(pDC, rc, nLineIndex, origin);
2010 return;
2013 CheckOtherView();
2015 // Draw the line
2017 pDC->SelectObject(GetFont(FALSE, FALSE));
2019 DrawTextLine(pDC, rc, nLineIndex, origin);
2021 // draw white space after the end of line
2022 CRect frect = rc;
2023 if (origin.x > frect.left)
2024 frect.left = origin.x;
2025 if (frect.right > frect.left)
2026 pDC->FillSolidRect(frect, crBkgnd);
2028 // draw the whitespace chars
2029 LPCTSTR pszChars = (LPCWSTR)sLine;
2030 if (m_bViewWhitespace)
2032 int xpos = 0;
2033 int nChars = 0;
2034 LPCTSTR pLastSpace = pszChars;
2035 int y = rc.top + (rc.bottom-rc.top)/2;
2036 xpos -= m_nOffsetChar * GetCharWidth();
2038 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2039 while (*pszChars)
2041 switch (*pszChars)
2043 case '\t':
2045 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2046 pLastSpace = pszChars + 1;
2047 // draw an arrow
2048 int nSpaces = GetTabSize() - nChars % GetTabSize();
2049 if (xpos + nSpaces * GetCharWidth() > 0)
2051 int xposreal = max(xpos, 0);
2052 if ((xposreal > 0) || (nSpaces > 0))
2054 CPen * oldPen = pDC->SelectObject(&pen);
2055 pDC->MoveTo(xposreal + rc.left, y);
2056 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2057 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y - 4);
2058 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2059 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y + 4);
2060 pDC->SelectObject(oldPen);
2063 xpos += nSpaces * GetCharWidth();
2064 nChars += nSpaces;
2066 break;
2067 case ' ':
2069 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2070 pLastSpace = pszChars + 1;
2071 if (xpos >= 0)
2073 const int whitespaceSize = 2;
2074 // draw 2-pixel rectangle, like Scintilla editor.
2075 pDC->FillSolidRect(xpos + rc.left + GetCharWidth() / 2 - whitespaceSize / 2, y, whitespaceSize, whitespaceSize, m_WhiteSpaceFg);
2077 xpos += GetCharWidth();
2078 nChars++;
2080 break;
2081 default:
2082 nChars++;
2083 break;
2085 pszChars++;
2088 DrawBlockLine(pDC, rc, nLineIndex);
2089 if (origin.x >= rc.left)
2090 DrawLineEnding(pDC, rc, nLineIndex, origin);
2093 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2095 if (nCount <= 0)
2097 line.Empty();
2098 return;
2101 int nTabSize = GetTabSize();
2103 int nActualOffset = CountExpandedChars(sLine, nOffset);
2105 LPCTSTR pszChars = (LPCWSTR)sLine;
2106 pszChars += nOffset;
2107 int nLength = nCount;
2109 int nTabCount = 0;
2110 for (int i=0; i<nLength; i++)
2112 if (pszChars[i] == L'\t')
2113 nTabCount ++;
2116 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2117 int nCurPos = 0;
2118 if (nTabCount > 0 || m_bViewWhitespace)
2120 for (int i=0; i<nLength; i++)
2122 if (pszChars[i] == L'\t')
2124 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2125 while (nSpaces > 0)
2127 pszBuf[nCurPos ++] = L' ';
2128 nSpaces --;
2131 else
2133 pszBuf[nCurPos] = pszChars[i];
2134 nCurPos ++;
2138 else
2140 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2141 nCurPos = nLength;
2143 pszBuf[nCurPos] = 0;
2144 line.ReleaseBuffer();
2147 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2149 CString sRet;
2150 int nLength = sLine.GetLength();
2151 ExpandChars(sLine, nOffset, nLength, sRet);
2152 return sRet;
2155 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2157 int nTabSize = GetTabSize();
2159 int nActualOffset = 0;
2160 for (int i=0; i<nLength; i++)
2162 if (sLine[i] == L'\t')
2163 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2164 else
2165 nActualOffset ++;
2167 return nActualOffset;
2170 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2172 if (m_pwndLeft)
2173 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2174 if (m_pwndRight)
2175 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2176 if (m_pwndBottom)
2177 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2178 if (m_pwndLocator)
2179 m_pwndLocator->Invalidate();
2182 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2184 //almost the same as ScrollAllToLine, but try to put the line in the
2185 //middle of the view, not on top
2186 int nNewTopLine = nNewLine - GetScreenLines()/2;
2187 if (nNewTopLine < 0)
2188 nNewTopLine = 0;
2189 if (nNewTopLine >= (int)m_Screen2View.size())
2190 nNewTopLine = (int)m_Screen2View.size()-1;
2191 if (bAll)
2192 ScrollAllToLine(nNewTopLine);
2193 else
2194 ScrollToLine(nNewTopLine);
2197 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2199 return TRUE;
2202 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2204 if (CView::OnCreate(lpCreateStruct) == -1)
2205 return -1;
2207 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2208 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2209 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2210 m_lfBaseFont.lfHeight = 0;
2211 m_lfBaseFont.lfWeight = FW_NORMAL;
2212 m_lfBaseFont.lfItalic = FALSE;
2213 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2214 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2215 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2216 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2217 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2219 return 0;
2222 void CBaseView::OnDestroy()
2224 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2226 m_pFindDialog->SendMessage(WM_CLOSE);
2227 return;
2229 CView::OnDestroy();
2230 DeleteFonts();
2231 ReleaseBitmap();
2234 void CBaseView::OnSize(UINT nType, int cx, int cy)
2236 CView::OnSize(nType, cx, cy);
2237 ReleaseBitmap();
2239 m_nScreenLines = -1;
2240 m_nScreenChars = -1;
2241 if (m_nLastScreenChars != GetScreenChars())
2243 BuildAllScreen2ViewVector();
2244 m_nLastScreenChars = m_nScreenChars;
2245 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2247 // if we're in wrap mode, the line wrapping most likely changed
2248 // and that means we have to redraw the whole window, not just the
2249 // scrolled part.
2250 Invalidate(FALSE);
2252 else
2254 // make sure the view header is redrawn
2255 CRect rcScroll;
2256 GetClientRect(&rcScroll);
2257 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2258 InvalidateRect(&rcScroll, FALSE);
2261 else
2263 // make sure the view header is redrawn
2264 CRect rcScroll;
2265 GetClientRect(&rcScroll);
2266 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2267 InvalidateRect(&rcScroll, FALSE);
2269 UpdateLocator();
2270 RecalcVertScrollBar();
2271 RecalcHorzScrollBar();
2273 UpdateCaret();
2276 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2278 if (m_pwndLeft)
2279 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2280 if (m_pwndRight)
2281 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2282 if (m_pwndBottom)
2283 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2284 if (m_pwndLocator)
2285 m_pwndLocator->Invalidate();
2286 return CView::OnMouseWheel(nFlags, zDelta, pt);
2289 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2291 if (m_pwndLeft)
2292 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2293 if (m_pwndRight)
2294 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2295 if (m_pwndBottom)
2296 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2297 if (m_pwndLocator)
2298 m_pwndLocator->Invalidate();
2301 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2303 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2304 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2306 if (bControl || bShift)
2308 if (m_pMainFrame->m_bWrapLines)
2309 return;
2310 // Ctrl-Wheel scrolls sideways
2311 ScrollSide(-zDelta/30);
2313 else
2315 ScrollVertical(zDelta);
2319 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2321 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2322 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2324 if (bControl || bShift)
2326 // Ctrl-H-Wheel scrolls vertical
2327 ScrollVertical(zDelta);
2329 else
2331 if (m_pMainFrame->m_bWrapLines)
2332 return;
2333 // Ctrl-Wheel scrolls sideways
2334 ScrollSide(zDelta/30);
2338 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2340 if (nHitTest == HTCLIENT)
2342 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2344 if (m_nMouseLine < (int)m_Screen2View.size())
2346 if (m_nMouseLine >= 0)
2348 int viewLine = GetViewLineForScreen(m_nMouseLine);
2349 if (viewLine < m_pViewData->GetCount())
2351 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2353 ::SetCursor(::LoadCursor(NULL, IDC_HAND));
2354 return TRUE;
2360 if (m_mouseInMargin)
2362 ::SetCursor(m_margincursor);
2363 return TRUE;
2365 if (m_nMouseLine >= 0)
2367 ::SetCursor(::LoadCursor(NULL, IDC_IBEAM)); // Set To Edit Cursor
2368 return TRUE;
2371 ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); // Set To Arrow Cursor
2372 return TRUE;
2374 return CView::OnSetCursor(pWnd, nHitTest, message);
2377 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2379 CView::OnKillFocus(pNewWnd);
2380 m_bFocused = FALSE;
2381 UpdateCaret();
2382 Invalidate();
2385 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2387 CView::OnSetFocus(pOldWnd);
2388 m_bFocused = TRUE;
2389 UpdateCaret();
2390 Invalidate();
2393 int CBaseView::GetLineFromPoint(CPoint point)
2395 ScreenToClient(&point);
2396 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2399 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2401 if (!this->IsWindowVisible())
2402 return;
2404 CIconMenu popup;
2405 if (!popup.CreatePopupMenu())
2406 return;
2408 AddContextItems(popup, state);
2410 CMenu popupEols;
2411 CMenu popupUnicode;
2412 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2413 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2414 if (IsWritable())
2416 CString temp;
2417 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2418 temp.LoadString(IDS_EDIT_TAB2SPACE);
2419 popup.AppendMenu(MF_STRING | (oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_TABTOSPACES, temp);
2420 temp.LoadString(IDS_EDIT_SPACE2TAB);
2421 popup.AppendMenu(MF_STRING | (oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_SPACESTOTABS, temp);
2422 temp.LoadString(IDS_EDIT_TRIM);
2423 popup.AppendMenu(MF_STRING | (oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2425 // add eol submenu
2426 if (!popupEols.CreatePopupMenu())
2427 return;
2429 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2430 for (int i = 1; i < _countof(eolArray); i++)
2432 temp = GetEolName(eolArray[i]);
2433 bool bChecked = (eEolType == eolArray[i]);
2434 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2437 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2438 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2440 // add encoding submenu
2441 if (!popupUnicode.CreatePopupMenu())
2442 return;
2443 for (int i = 0; i < _countof(uctArray); i++)
2445 temp = CFileTextLines::GetEncodingName(uctArray[i]);
2446 bool bChecked = (m_texttype == uctArray[i]);
2447 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2449 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2450 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2454 CompensateForKeyboard(point);
2456 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
2457 ResetUndoStep();
2458 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2460 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2462 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2464 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2465 SaveUndoStep();
2467 switch (cmd)
2469 // 2-pane view commands; target is right view
2470 case POPUPCOMMAND_USELEFTBLOCK:
2471 m_pwndRight->UseLeftBlock();
2472 break;
2473 case POPUPCOMMAND_USELEFTFILE:
2474 m_pwndRight->UseLeftFile();
2475 break;
2476 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2477 m_pwndRight->UseBothLeftFirst();
2478 break;
2479 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2480 m_pwndRight->UseBothRightFirst();
2481 break;
2482 case POPUPCOMMAND_MARKBLOCK:
2483 m_pwndRight->MarkBlock(true);
2484 break;
2485 case POPUPCOMMAND_UNMARKBLOCK:
2486 m_pwndRight->MarkBlock(false);
2487 break;
2488 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2489 m_pwndRight->LeaveOnlyMarkedBlocks();
2490 break;
2491 // 2-pane view multiedit commands; target is left view
2492 case POPUPCOMMAND_PREPENDFROMRIGHT:
2493 if (!m_pwndLeft->IsReadonly())
2494 m_pwndLeft->UseBothRightFirst();
2495 break;
2496 case POPUPCOMMAND_REPLACEBYRIGHT:
2497 if (!m_pwndLeft->IsReadonly())
2498 m_pwndLeft->UseRightBlock();
2499 break;
2500 case POPUPCOMMAND_APPENDFROMRIGHT:
2501 if (!m_pwndLeft->IsReadonly())
2502 m_pwndLeft->UseBothLeftFirst();
2503 break;
2504 case POPUPCOMMAND_USERIGHTFILE:
2505 m_pwndLeft->UseRightFile();
2506 break;
2507 // 3-pane view commands; target is bottom view
2508 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2509 m_pwndBottom->UseBothRightFirst();
2510 break;
2511 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2512 m_pwndBottom->UseBothLeftFirst();
2513 break;
2514 case POPUPCOMMAND_USEYOURBLOCK:
2515 m_pwndBottom->UseRightBlock();
2516 break;
2517 case POPUPCOMMAND_USEYOURFILE:
2518 m_pwndBottom->UseRightFile();
2519 break;
2520 case POPUPCOMMAND_USETHEIRBLOCK:
2521 m_pwndBottom->UseLeftBlock();
2522 break;
2523 case POPUPCOMMAND_USETHEIRFILE:
2524 m_pwndBottom->UseLeftFile();
2525 break;
2526 // copy, cut and paste commands
2527 case ID_EDIT_COPY:
2528 OnEditCopy();
2529 break;
2530 case ID_EDIT_CUT:
2531 OnEditCut();
2532 break;
2533 case ID_EDIT_PASTE:
2534 OnEditPaste();
2535 break;
2536 // white chars manipulations
2537 case POPUPCOMMAND_TABTOSPACES:
2538 ConvertTabToSpaces();
2539 break;
2540 case POPUPCOMMAND_SPACESTOTABS:
2541 Tabularize();
2542 break;
2543 case POPUPCOMMAND_REMOVETRAILWHITES:
2544 RemoveTrailWhiteChars();
2545 break;
2546 default:
2547 return;
2548 } // switch (cmd)
2549 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2550 return;
2553 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2555 if (!m_pViewData)
2556 return;
2558 int nViewBlockStart = -1;
2559 int nViewBlockEnd = -1;
2560 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2561 if ((point.x >= 0) && (point.y >= 0))
2563 int nLine = GetLineFromPoint(point)-1;
2564 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2566 int nViewLine = GetViewLineForScreen(nLine);
2567 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2569 ClearSelection(); // Clear text-copy selection
2571 nViewBlockStart = nViewLine;
2572 nViewBlockEnd = nViewLine;
2573 DiffStates state = m_pViewData->GetState(nViewLine);
2574 while (nViewBlockStart > 0)
2576 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2577 if (!LinesInOneChange(-1, state, lineState))
2578 break;
2579 nViewBlockStart--;
2582 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2584 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2585 if (!LinesInOneChange(1, state, lineState))
2586 break;
2587 nViewBlockEnd++;
2590 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2591 UpdateCaretPosition(point);
2596 // FixSelection(); fix selection range
2597 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2598 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2600 DiffStates state = DIFFSTATE_UNKNOWN;
2601 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2603 // find a more 'relevant' state in the selection
2604 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2606 state = m_pViewData->GetState(i);
2607 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2608 break;
2611 OnContextMenu(point, state);
2614 void CBaseView::RefreshViews()
2616 if (m_pwndLeft)
2618 m_pwndLeft->UpdateStatusBar();
2619 m_pwndLeft->Invalidate();
2621 if (m_pwndRight)
2623 m_pwndRight->UpdateStatusBar();
2624 m_pwndRight->Invalidate();
2626 if (m_pwndBottom)
2628 m_pwndBottom->UpdateStatusBar();
2629 m_pwndBottom->Invalidate();
2631 if (m_pwndLocator)
2632 m_pwndLocator->Invalidate();
2635 void CBaseView::GoToFirstDifference()
2637 SetCaretToFirstViewLine();
2638 SelectNextBlock(1, false, false);
2641 void CBaseView::GoToFirstConflict()
2643 SetCaretToFirstViewLine();
2644 SelectNextBlock(1, true, false);
2647 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2649 ClearSelection();
2650 SetupAllSelection(nStart, max(nStart, nEnd));
2652 UpdateCaretPosition(SetupPoint(0, nStart));
2653 Invalidate();
2656 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2658 ClearSelection();
2659 SetupAllViewSelection(nStart, max(nStart, nEnd));
2661 UpdateCaretViewPosition(SetupPoint(0, nStart));
2662 Invalidate();
2665 void CBaseView::SetupAllViewSelection(int start, int end)
2667 SetupViewSelection(m_pwndBottom, start, end);
2668 SetupViewSelection(m_pwndLeft, start, end);
2669 SetupViewSelection(m_pwndRight, start, end);
2672 void CBaseView::SetupAllSelection(int start, int end)
2674 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2677 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2679 void CBaseView::SetupSelection(int start, int end)
2681 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2684 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2686 if (!IsViewGood(view))
2687 return;
2688 view->SetupViewSelection(start, end);
2691 void CBaseView::SetupViewSelection(int start, int end)
2693 // clear text selection before setting line selection ?
2694 m_nSelViewBlockStart = start;
2695 m_nSelViewBlockEnd = end;
2696 Invalidate();
2700 void CBaseView::OnMergePreviousconflict()
2702 SelectNextBlock(-1, true);
2705 void CBaseView::OnMergeNextconflict()
2707 SelectNextBlock(1, true);
2710 void CBaseView::OnMergeNextdifference()
2712 SelectNextBlock(1, false);
2715 void CBaseView::OnMergePreviousdifference()
2717 SelectNextBlock(-1, false);
2720 bool CBaseView::HasNextConflict()
2722 return SelectNextBlock(1, true, true, true);
2725 bool CBaseView::HasPrevConflict()
2727 return SelectNextBlock(-1, true, true, true);
2730 bool CBaseView::HasNextDiff()
2732 return SelectNextBlock(1, false, true, true);
2735 bool CBaseView::HasPrevDiff()
2737 return SelectNextBlock(-1, false, true, true);
2740 bool CBaseView::LinesInOneChange(int direction,
2741 DiffStates initialLineState, DiffStates currentLineState)
2743 // Checks whether all the adjacent lines starting from the initial line
2744 // and up to the current line form the single change
2746 // First of all, if the two lines have identical states, they surely
2747 // belong to one change.
2748 if (initialLineState == currentLineState)
2749 return true;
2751 // Either we move down and initial line state is "added" or "removed" and
2752 // current line state is "empty"...
2753 if (direction > 0)
2755 if (currentLineState == DIFFSTATE_EMPTY)
2757 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2758 return true;
2760 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2761 return true;
2763 // ...or we move up and initial line state is "empty" and current line
2764 // state is "added" or "removed".
2765 if (direction < 0)
2767 if (initialLineState == DIFFSTATE_EMPTY)
2769 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2770 return true;
2772 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2773 return true;
2775 return false;
2778 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2780 if (! m_pViewData)
2781 return false;
2783 const int linesCount = (int)m_Screen2View.size();
2784 if(linesCount == 0)
2785 return false;
2787 int nCenterPos = GetCaretPosition().y;
2788 int nLimit = -1;
2789 if (nDirection > 0)
2790 nLimit = linesCount;
2792 if (nCenterPos >= linesCount)
2793 nCenterPos = linesCount-1;
2795 if (bSkipEndOfCurrentBlock)
2797 // Find end of current block
2798 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2799 while (nCenterPos != nLimit)
2801 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2802 if (!LinesInOneChange(nDirection, state, lineState))
2803 break;
2804 nCenterPos += nDirection;
2808 // Find next diff/conflict block
2809 while (nCenterPos != nLimit)
2811 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2812 if (!bConflict &&
2813 (linestate != DIFFSTATE_NORMAL) &&
2814 (linestate != DIFFSTATE_UNKNOWN))
2816 break;
2818 if (bConflict &&
2819 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2820 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2821 (linestate == DIFFSTATE_CONFLICTED) ||
2822 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2824 break;
2827 nCenterPos += nDirection;
2829 if (nCenterPos == nLimit)
2830 return false;
2831 if (dryrun)
2832 return (nCenterPos != nLimit);
2834 // Find end of new block
2835 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2836 int nBlockEnd = nCenterPos;
2837 const int maxAllowedLine = nLimit-nDirection;
2838 while (nBlockEnd != maxAllowedLine)
2840 const int lineIndex = nBlockEnd + nDirection;
2841 if (lineIndex >= linesCount)
2842 break;
2843 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2844 if (!LinesInOneChange(nDirection, state, lineState))
2845 break;
2846 nBlockEnd += nDirection;
2849 int nTopPos = nCenterPos - (GetScreenLines()/2);
2850 if (nTopPos < 0)
2851 nTopPos = 0;
2853 POINT ptCaretPos = {0, nCenterPos};
2854 SetCaretPosition(ptCaretPos);
2855 ClearSelection();
2856 if (nDirection > 0)
2857 SetupAllSelection(nCenterPos, nBlockEnd);
2858 else
2859 SetupAllSelection(nBlockEnd, nCenterPos);
2861 ScrollAllToLine(nTopPos, FALSE);
2862 RecalcAllVertScrollBars(TRUE);
2863 SetCaretToLineStart();
2864 EnsureCaretVisible();
2865 OnNavigateNextinlinediff();
2867 UpdateViewsCaretPosition();
2868 UpdateCaret();
2869 ShowDiffLines(nCenterPos);
2870 return true;
2873 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2875 if (pNMHDR->idFrom != (UINT_PTR)m_hWnd)
2876 return FALSE;
2878 CString strTipText;
2879 strTipText = m_sWindowName + L"\r\n" + m_sFullFilePath;
2881 DWORD pos = GetMessagePos();
2882 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2883 ScreenToClient(&point);
2884 const int nLine = GetButtonEventLineIndex(point);
2886 if (nLine >= 0)
2888 int nViewLine = GetViewLineForScreen(nLine);
2889 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2891 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2892 if (movedIndex >= 0)
2894 if (m_pViewData->IsMovedFrom(nViewLine))
2896 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2898 else
2900 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2907 *pResult = 0;
2908 if (strTipText.IsEmpty())
2909 return TRUE;
2911 // need to handle both ANSI and UNICODE versions of the message
2912 if (pNMHDR->code == TTN_NEEDTEXTA)
2914 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2915 pTTTA->lpszText = m_szTip;
2916 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2918 else
2920 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2921 lstrcpyn(m_wszTip, strTipText, min(strTipText.GetLength() + 1, _countof(m_wszTip) - 1));
2922 pTTTW->lpszText = m_wszTip;
2925 return TRUE; // message was handled
2928 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2930 CRect rcClient;
2931 GetClientRect(rcClient);
2932 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2933 int marginwidth = MARGINWIDTH;
2934 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2936 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2938 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2940 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2942 // inside the header part of the view (showing the filename)
2943 pTI->hwnd = this->m_hWnd;
2944 this->GetClientRect(&pTI->rect);
2945 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2946 pTI->uId = (UINT_PTR)m_hWnd;
2947 pTI->lpszText = LPSTR_TEXTCALLBACK;
2949 // we want multi line tooltips
2950 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2951 if (pToolTip->GetSafeHwnd() != NULL)
2953 pToolTip->SetMaxTipWidth(INT_MAX);
2956 return (textrect.PtInRect(point) ? 1 : 2);
2959 return -1;
2962 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2964 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2965 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2967 switch (nChar)
2969 case VK_TAB:
2970 if (bControl)
2972 if (this==m_pwndLeft)
2974 if (IsViewGood(m_pwndRight))
2976 m_pwndRight->SetFocus();
2978 else if (IsViewGood(m_pwndBottom))
2980 m_pwndBottom->SetFocus();
2983 else if (this==m_pwndRight)
2985 if (IsViewGood(m_pwndBottom))
2987 m_pwndBottom->SetFocus();
2989 else if (IsViewGood(m_pwndLeft))
2991 m_pwndLeft->SetFocus();
2994 else if (this==m_pwndBottom)
2996 if (IsViewGood(m_pwndLeft))
2998 m_pwndLeft->SetFocus();
3000 else if (IsViewGood(m_pwndRight))
3002 m_pwndRight->SetFocus();
3006 break;
3007 case VK_PRIOR:
3009 POINT ptCaretPos = GetCaretPosition();
3010 ptCaretPos.y -= GetScreenLines();
3011 ptCaretPos.y = max(ptCaretPos.y, 0);
3012 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3013 SetCaretPosition(ptCaretPos);
3014 OnCaretMove(MOVELEFT, bShift);
3015 ShowDiffLines(ptCaretPos.y);
3017 break;
3018 case VK_NEXT:
3020 POINT ptCaretPos = GetCaretPosition();
3021 ptCaretPos.y += GetScreenLines();
3022 if (ptCaretPos.y >= GetLineCount())
3023 ptCaretPos.y = GetLineCount()-1;
3024 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3025 SetCaretPosition(ptCaretPos);
3026 OnCaretMove(MOVERIGHT, bShift);
3027 ShowDiffLines(ptCaretPos.y);
3029 break;
3030 case VK_HOME:
3032 if (bControl)
3034 ScrollAllToLine(0);
3035 SetCaretToViewStart();
3036 m_nCaretGoalPos = 0;
3037 if (bShift)
3038 AdjustSelection(MOVELEFT);
3039 else
3040 ClearSelection();
3041 UpdateCaret();
3043 else
3045 POINT ptCaretPos = GetCaretPosition();
3046 CString sLine = GetLineChars(ptCaretPos.y);
3047 int pos = 0;
3048 while (pos < sLine.GetLength())
3050 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3051 break;
3052 ++pos;
3054 if (ptCaretPos.x == pos)
3056 SetCaretToLineStart();
3057 m_nCaretGoalPos = 0;
3058 OnCaretMove(MOVERIGHT, bShift);
3059 ScrollAllToChar(0);
3061 else
3063 ptCaretPos.x = pos;
3064 SetCaretAndGoalPosition(ptCaretPos);
3065 OnCaretMove(MOVELEFT, bShift);
3069 break;
3070 case VK_END:
3072 if (bControl)
3074 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3075 POINT ptCaretPos;
3076 ptCaretPos.y = GetLineCount()-1;
3077 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3078 SetCaretAndGoalPosition(ptCaretPos);
3079 if (bShift)
3080 AdjustSelection(MOVERIGHT);
3081 else
3082 ClearSelection();
3084 else
3086 POINT ptCaretPos = GetCaretPosition();
3087 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3088 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3090 ptCaretPos.x--;
3092 SetCaretAndGoalPosition(ptCaretPos);
3093 OnCaretMove(MOVERIGHT, bShift);
3096 break;
3097 case VK_BACK:
3098 if (IsWritable())
3100 if (! HasTextSelection())
3102 POINT ptCaretPos = GetCaretPosition();
3103 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3104 break;
3105 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3106 if (bControl)
3107 MoveCaretWordLeft();
3108 else
3110 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3114 m_ptSelectionViewPosStart = GetCaretViewPosition();
3116 RemoveSelectedText();
3118 break;
3119 case VK_DELETE:
3120 if (IsWritable())
3122 if (! HasTextSelection())
3124 if (bControl)
3126 m_ptSelectionViewPosStart = GetCaretViewPosition();
3127 MoveCaretWordRight();
3128 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3130 else
3132 if (! MoveCaretRight())
3133 break;
3134 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3135 MoveCaretLeft();
3136 m_ptSelectionViewPosStart = GetCaretViewPosition();
3139 RemoveSelectedText();
3141 break;
3142 case VK_INSERT:
3143 m_bInsertMode = !m_bInsertMode;
3144 UpdateCaret();
3145 break;
3147 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3150 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3152 const int nClickedLine = GetButtonEventLineIndex(point);
3153 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3155 POINT ptCaretPos;
3156 ptCaretPos.y = nClickedLine;
3157 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3158 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3159 SetCaretAndGoalPosition(ptCaretPos);
3161 if (nFlags & MK_SHIFT)
3162 AdjustSelection(MOVERIGHT);
3163 else
3165 ClearSelection();
3166 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3167 if (point.x < GetMarginWidth())
3169 // select the whole line
3170 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3171 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3175 UpdateViewsCaretPosition();
3176 Invalidate();
3179 CView::OnLButtonDown(nFlags, point);
3182 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3184 if (zChar == ' ' || zChar == '\t' )
3186 return CHG_WHITESPACE;
3188 if (zChar < 0x20)
3190 return CHG_CONTROL;
3192 if (m_sWordSeparators.Find(zChar) >= 0)
3194 return CHG_WORDSEPARATOR;
3196 return CHG_WORDLETTER;
3199 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3201 if (m_pViewData == 0) {
3202 CView::OnLButtonDblClk(nFlags, point);
3203 return;
3206 const int nClickedLine = GetButtonEventLineIndex(point);
3207 if ( nClickedLine < 0)
3208 return;
3209 int nViewLine = GetViewLineForScreen(nClickedLine);
3210 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3212 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3214 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3216 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3217 int screenLine = FindViewLineNumber(movedindex);
3218 int nTop = screenLine - GetScreenLines()/2;
3219 if (nTop < 0)
3220 nTop = 0;
3221 ScrollAllToLine(nTop);
3222 // find and select the whole moved block
3223 int startSel = movedindex;
3224 int endSel = movedindex;
3225 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3226 startSel--;
3227 startSel++;
3228 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3229 endSel++;
3230 endSel--;
3231 m_pOtherView->SetupSelection(startSel, endSel);
3232 return CView::OnLButtonDblClk(nFlags, point);
3236 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3238 // a double click on a marker expands the hidden text
3239 int i = nViewLine;
3240 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3242 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3243 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3244 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3245 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3246 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3247 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3248 i++;
3250 BuildAllScreen2ViewVector();
3251 if (m_pwndLeft)
3252 m_pwndLeft->Invalidate();
3253 if (m_pwndRight)
3254 m_pwndRight->Invalidate();
3255 if (m_pwndBottom)
3256 m_pwndBottom->Invalidate();
3258 else
3260 POINT ptCaretPos;
3261 ptCaretPos.y = nClickedLine;
3262 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3263 SetCaretPosition(ptCaretPos);
3264 ClearSelection();
3266 POINT ptViewCarret = GetCaretViewPosition();
3267 nViewLine = ptViewCarret.y;
3268 if (nViewLine >= GetViewCount())
3269 return;
3270 const CString &sLine = GetViewLine(nViewLine);
3271 int nLineLength = sLine.GetLength();
3272 int nBasePos = ptViewCarret.x;
3273 // get target char group
3274 ECharGroup eLeft = CHG_UNKNOWN;
3275 if (nBasePos > 0)
3277 eLeft = GetCharGroup(sLine[nBasePos-1]);
3279 ECharGroup eRight = CHG_UNKNOWN;
3280 if (nBasePos < nLineLength)
3282 eRight = GetCharGroup(sLine[nBasePos]);
3284 ECharGroup eTarget = max(eRight, eLeft);
3285 // find left margin
3286 int nLeft = nBasePos;
3287 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3289 nLeft--;
3291 // get right margin
3292 int nRight = nBasePos;
3293 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3295 nRight++;
3297 // set selection
3298 m_ptSelectionViewPosStart.x = nLeft;
3299 m_ptSelectionViewPosStart.y = nViewLine;
3300 m_ptSelectionViewPosEnd.x = nRight;
3301 m_ptSelectionViewPosEnd.y = nViewLine;
3302 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3303 SetupAllViewSelection(nViewLine, nViewLine);
3304 // set caret
3305 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3306 UpdateViewsCaretPosition();
3307 UpdateGoalPos();
3309 // set mark word
3310 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3311 int nMarkWidth = max(nRight - nLeft, 0);
3312 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3313 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3315 m_sMarkedWord.Empty();
3318 if (m_pwndLeft)
3319 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3320 if (m_pwndRight)
3321 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3322 if (m_pwndBottom)
3323 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3325 Invalidate();
3326 if (m_pwndLocator)
3327 m_pwndLocator->Invalidate();
3330 CView::OnLButtonDblClk(nFlags, point);
3333 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3335 const int nClickedLine = GetButtonEventLineIndex(point);
3336 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3338 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3340 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3341 if (pidl)
3343 SHOpenFolderAndSelectItems(pidl,0,0,0);
3344 CoTaskMemFree((LPVOID)pidl);
3347 return;
3349 POINT ptCaretPos;
3350 ptCaretPos.y = nClickedLine;
3351 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3352 SetCaretAndGoalPosition(ptCaretPos);
3353 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3354 if (m_pwndLeft)
3355 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3356 if (m_pwndRight)
3357 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3358 if (m_pwndBottom)
3359 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3360 ClearSelection();
3361 m_ptSelectionViewPosStart.x = 0;
3362 m_ptSelectionViewPosStart.y = nClickedLine;
3363 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3364 m_ptSelectionViewPosEnd.y = nClickedLine;
3365 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3366 UpdateViewsCaretPosition();
3367 Invalidate();
3368 if (m_pwndLocator)
3369 m_pwndLocator->Invalidate();
3372 void CBaseView::OnEditCopy()
3374 CString sCopyData = GetSelectedText();
3376 if (!sCopyData.IsEmpty())
3378 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3382 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3384 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3386 --m_pMainFrame->m_nMoveMovesToIgnore;
3387 CView::OnMouseMove(nFlags, point);
3388 return;
3390 int nMouseLine = GetButtonEventLineIndex(point);
3391 if (nMouseLine < -1)
3392 nMouseLine = -1;
3393 m_mouseInMargin = point.x < GetMarginWidth();
3395 ShowDiffLines(nMouseLine);
3397 KillTimer(IDT_SCROLLTIMER);
3398 if (nFlags & MK_LBUTTON)
3400 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3401 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3402 if (saveMouseLine < 0)
3403 return;
3404 int col = CalcColFromPoint(point.x, saveMouseLine);
3405 int charIndex = CalculateCharIndex(saveMouseLine, col);
3406 if (HasSelection() &&
3407 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3409 POINT ptCaretPos = {charIndex, nMouseLine};
3410 SetCaretAndGoalPosition(ptCaretPos);
3411 AdjustSelection(MOVERIGHT);
3412 Invalidate();
3413 UpdateWindow();
3415 if (nMouseLine < m_nTopLine)
3417 ScrollAllToLine(m_nTopLine-1, TRUE);
3418 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3420 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3422 ScrollAllToLine(m_nTopLine+1, TRUE);
3423 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3425 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3427 ScrollAllSide(-1);
3428 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3430 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3432 ScrollAllSide(1);
3433 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3435 SetCapture();
3439 CView::OnMouseMove(nFlags, point);
3442 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3444 ShowDiffLines(-1);
3445 ReleaseCapture();
3446 KillTimer(IDT_SCROLLTIMER);
3448 __super::OnLButtonUp(nFlags, point);
3451 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3453 if (nIDEvent == IDT_SCROLLTIMER)
3455 POINT point;
3456 GetCursorPos(&point);
3457 ScreenToClient(&point);
3458 int nMouseLine = GetButtonEventLineIndex(point);
3459 if (nMouseLine < -1)
3461 nMouseLine = -1;
3463 if (GetKeyState(VK_LBUTTON)&0x8000)
3465 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3466 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3467 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3468 if (nMouseLine < m_nTopLine)
3470 ScrollAllToLine(m_nTopLine-1, TRUE);
3471 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3473 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3475 ScrollAllToLine(m_nTopLine+1, TRUE);
3476 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3478 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3480 ScrollAllSide(-1);
3481 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3483 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3485 ScrollAllSide(1);
3486 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3492 CView::OnTimer(nIDEvent);
3495 void CBaseView::ShowDiffLines(int nLine)
3497 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3499 m_pwndLineDiffBar->ShowLines(nLine);
3500 nLine = -1;
3501 m_nMouseLine = nLine;
3502 return;
3505 if ((!m_pwndRight)||(!m_pwndLeft))
3506 return;
3507 if(m_pMainFrame->m_bOneWay)
3508 return;
3510 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3511 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3513 if (nLine < 0)
3514 return;
3516 if (nLine != m_nMouseLine)
3518 if (nLine >= GetLineCount())
3519 nLine = -1;
3520 m_nMouseLine = nLine;
3521 m_pwndLineDiffBar->ShowLines(nLine);
3523 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3526 const viewdata& CBaseView::GetEmptyLineData()
3528 static const viewdata emptyLine(L"", DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3529 return emptyLine;
3532 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3534 for (int i = 0; i < nCount; i++)
3536 InsertViewData(nFirstView, GetEmptyLineData());
3541 void CBaseView::UpdateCaret()
3543 POINT ptCaretPos = GetCaretPosition();
3544 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3545 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3546 SetCaretPosition(ptCaretPos);
3548 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3550 if (m_bFocused &&
3551 ptCaretPos.y >= m_nTopLine &&
3552 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3553 nCaretOffset >= m_nOffsetChar &&
3554 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3556 POINT pt1 = TextToClient(ptCaretPos);
3557 if (m_bInsertMode)
3558 CreateSolidCaret(2, GetLineHeight());
3559 else
3561 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3562 POINT pt2 = TextToClient(pt);
3563 int width = max(GetCharWidth(), pt2.x - pt1.x);
3564 CreateSolidCaret(width, GetLineHeight());
3566 SetCaretPos(pt1);
3567 ShowCaret();
3569 else
3571 HideCaret();
3575 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3577 POINT ptViewPos;
3578 ptViewPos.x = pt.x;
3580 int nSubLine = GetSubLineOffset(pt.y);
3581 if (nSubLine > 0)
3583 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3585 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3589 ptViewPos.y = GetViewLineForScreen(pt.y);
3590 return ptViewPos;
3593 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3595 POINT ptPos;
3596 int nViewLineLenLeft = GetViewLineLength(pt.y);
3597 ptPos.x = min(nViewLineLenLeft, pt.x);
3598 ptPos.y = FindScreenLineForViewLine(pt.y);
3599 if (GetViewLineForScreen(ptPos.y) != pt.y )
3601 ptPos.x = 0;
3603 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3605 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3606 while (nSubLineLength < ptPos.x)
3608 ptPos.x -= nSubLineLength;
3609 nViewLineLenLeft -= nSubLineLength;
3610 ptPos.y++;
3611 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3613 // last pos of non last sub-line go to start of next screen line
3614 // Note: while this works correctly, it's not what a user might expect:
3615 // cursor-right when the caret is before the last char of a wrapped line
3616 // now moves the caret to the next line. But users expect the caret to
3617 // move to the right of the last char instead, and with another cursor-right
3618 // keystroke to move the caret to the next line.
3619 // Basically, this would require to handle two caret positions for the same
3620 // logical position in the line string (one on the last position of the first line,
3621 // one on the first position of the new line. For non-wrapped lines this works
3622 // because there's an 'invisible' newline char at the end of the first line.
3623 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3625 ptPos.x = 0;
3626 ptPos.y++;
3630 return ptPos;
3634 void CBaseView::EnsureCaretVisible()
3636 POINT ptCaretPos = GetCaretPosition();
3637 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3639 if (ptCaretPos.y < m_nTopLine)
3640 ScrollAllToLine(ptCaretPos.y);
3641 int screnLines = GetScreenLines();
3642 if (screnLines)
3644 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3645 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3646 if (nCaretOffset < m_nOffsetChar)
3647 ScrollAllToChar(nCaretOffset);
3648 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3649 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3653 int CBaseView::CalculateActualOffset(const POINT& point)
3655 int nLineIndex = point.y;
3656 int nCharIndex = point.x;
3657 ASSERT(nCharIndex >= 0);
3658 CString sLine = GetLineChars(nLineIndex);
3659 int nLineLength = sLine.GetLength();
3660 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3663 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3665 int nLength = GetLineLength(nLineIndex);
3666 int nSubLine = GetSubLineOffset(nLineIndex);
3667 if (nSubLine>=0)
3669 int nViewLine = GetViewLineForScreen(nLineIndex);
3670 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3672 int nMultilineCount = CountMultiLines(nViewLine);
3673 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3675 nLength--;
3679 CString Line = GetLineChars(nLineIndex);
3680 int nIndex = 0;
3681 int nOffset = 0;
3682 int nTabSize = GetTabSize();
3683 while (nOffset < nActualOffset && nIndex < nLength)
3685 if (Line.GetAt(nIndex) == L'\t')
3686 nOffset += (nTabSize - nOffset % nTabSize);
3687 else
3688 ++nOffset;
3689 ++nIndex;
3691 return nIndex;
3695 * @param xpos X coordinate in CBaseView
3696 * @param lineIndex logical line index (e.g. wrap/collapse)
3698 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3700 int xpos2;
3701 CDC *pDC = GetDC();
3702 if (pDC != nullptr)
3704 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3705 int fit = text.GetLength();
3706 auto posBuffer = std::make_unique<int[]>(fit);
3707 pDC->SelectObject(GetFont()); // is this right font ?
3708 SIZE size;
3709 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3710 ReleaseDC(pDC);
3711 int lower = -1, upper = fit - 1;
3712 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3715 int middle = (upper + lower + 1) / 2;
3716 int width = posBuffer.get()[middle];
3717 if (xcheck < width)
3718 upper = middle - 1;
3719 else
3720 lower = middle;
3721 } while (lower < upper);
3722 lower++;
3723 xpos2 = lower;
3724 if (lower < fit - 1)
3726 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3727 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3728 xpos2++;
3731 else
3733 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3734 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3735 xpos2++;
3737 return xpos2;
3740 POINT CBaseView::TextToClient(const POINT& point)
3742 POINT pt;
3743 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3744 pt.y = nOffsetScreenLine * GetLineHeight();
3745 pt.x = CalculateActualOffset(point);
3747 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3748 CDC * pDC = GetDC();
3749 if (pDC)
3751 pDC->SelectObject(GetFont()); // is this right font ?
3752 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3753 CString sLine = GetLineChars(nScreenLine);
3754 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3755 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3756 ReleaseDC(pDC);
3757 } else {
3758 nLeft += pt.x * GetCharWidth();
3761 pt.x = nLeft;
3762 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3763 return pt;
3766 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3768 CView::OnChar(nChar, nRepCnt, nFlags);
3770 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3771 bool bSkipSelectionClear = false;
3773 if (IsReadonly())
3774 return;
3776 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3777 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3779 return;
3782 if (!m_pViewData) // no data - nothing to do
3783 return;
3785 if (nChar == VK_F16)
3787 // generated by a ctrl+backspace - ignore.
3789 else if (nChar==VK_TAB && HasTextLineSelection())
3791 // change indentation for selected lines
3792 if (bShift)
3794 RemoveIndentationForSelectedBlock();
3796 else
3798 AddIndentationForSelectedBlock();
3800 bSkipSelectionClear = true;
3802 else if ((nChar > 31)||(nChar == VK_TAB))
3804 ResetUndoStep();
3805 RemoveSelectedText();
3806 POINT ptCaretViewPos = GetCaretViewPosition();
3807 int nViewLine = ptCaretViewPos.y;
3808 if ((nViewLine==0)&&(GetViewCount()==0))
3809 OnChar(VK_RETURN, 0, 0);
3810 int charCount = 1;
3811 viewdata lineData = GetViewData(nViewLine);
3812 if (nChar == VK_TAB)
3814 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3815 if (indentChars > 0)
3817 lineData.sLine.Insert(ptCaretViewPos.x, CString(L' ', indentChars));
3818 charCount = indentChars;
3820 else
3821 lineData.sLine.Insert(ptCaretViewPos.x, L'\t');
3823 else
3825 if (m_bInsertMode)
3826 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3827 else
3829 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3830 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3831 else
3832 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3835 if (IsStateEmpty(lineData.state))
3837 // if not last line set EOL
3838 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3840 if (!IsViewLineEmpty(nCheckViewLine))
3842 lineData.ending = m_lineendings;
3843 break;
3846 // make sure previous (non empty) line have EOL set
3847 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3849 if (!IsViewLineEmpty(nCheckViewLine))
3851 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3853 SetViewLineEnding(nCheckViewLine, m_lineendings);
3855 break;
3859 lineData.state = DIFFSTATE_EDITED;
3860 bool bNeedRenumber = false;
3861 if (lineData.linenumber == -1)
3863 lineData.linenumber = 0;
3864 bNeedRenumber = true;
3866 SetViewData(nViewLine, lineData);
3867 SetModified();
3868 SaveUndoStep();
3869 BuildAllScreen2ViewVector(nViewLine);
3870 if (bNeedRenumber)
3872 UpdateViewLineNumbers();
3874 for (int i = 0; i < charCount; ++i)
3875 MoveCaretRight();
3876 UpdateGoalPos();
3878 else if (nChar == 10)
3880 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3881 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3882 EOL newEOL = EOL_CRLF;
3883 switch (eol)
3885 case EOL_CRLF:
3886 newEOL = EOL_CR;
3887 break;
3888 case EOL_CR:
3889 newEOL = EOL_LF;
3890 break;
3891 case EOL_LF:
3892 newEOL = EOL_CRLF;
3893 break;
3895 if (eol==EOL_NOENDING || eol==newEOL)
3896 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3897 // to add EOL on newly edited empty line hit enter
3898 // don't store into UNDO if no change happened
3899 // and don't mark file as modified
3900 return;
3901 AddUndoViewLine(nViewLine);
3902 m_pViewData->SetLineEnding(nViewLine, newEOL);
3903 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3904 UpdateGoalPos();
3906 else if (nChar == VK_RETURN)
3908 // insert a new, fresh and empty line below the cursor
3909 RemoveSelectedText();
3911 CUndo::GetInstance().BeginGrouping();
3913 POINT ptCaretViewPos = GetCaretViewPosition();
3914 int nViewLine = ptCaretViewPos.y;
3915 int nLeft = ptCaretViewPos.x;
3916 CString sLine = GetViewLineChars(nViewLine);
3917 CString sLineLeft = sLine.Left(nLeft);
3918 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3919 EOL eOriginalEnding = EOL_AUTOLINE;
3920 if (m_pViewData->GetCount() > nViewLine)
3921 eOriginalEnding = GetViewLineEnding(nViewLine);
3923 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3925 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3926 SetViewData(nViewLine, newFirstLine);
3929 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3930 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3931 InsertViewData(nInsertLine, newLastLine);
3932 SetModified();
3933 SaveUndoStep();
3935 // adds new line everywhere except me
3936 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3938 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3940 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3942 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3944 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3946 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3948 SaveUndoStep();
3950 UpdateViewLineNumbers();
3951 SaveUndoStep();
3952 CUndo::GetInstance().EndGrouping();
3954 BuildAllScreen2ViewVector();
3955 // move the cursor to the new line
3956 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3957 SetCaretAndGoalViewPosition(ptCaretViewPos);
3959 else
3960 return; // Unknown control character -- ignore it.
3961 if (!bSkipSelectionClear)
3962 ClearSelection();
3963 EnsureCaretVisible();
3964 UpdateCaret();
3965 Invalidate(FALSE);
3968 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3970 ResetUndoStep();
3971 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3972 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3973 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3974 SetModified();
3975 SaveUndoStep();
3976 RecalcAllVertScrollBars();
3977 Invalidate(FALSE);
3980 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3982 if (m_pViewData == NULL)
3983 return;
3984 int viewLine = nViewLineIndex;
3985 EOL ending = m_pViewData->GetLineEnding(viewLine);
3986 if (ending == EOL_NOENDING)
3988 ending = m_lineendings;
3990 viewdata newLine(L"", DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3991 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3993 CString sPartLine = GetViewLineChars(nViewLineIndex);
3994 int nPosx = GetCaretPosition().x; // should be view pos ?
3995 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3996 sPartLine = sPartLine.Mid(nPosx);
3997 newLine.sLine = sPartLine;
3999 m_pViewData->InsertData(viewLine+1, newLine);
4000 BuildAllScreen2ViewVector();
4003 void CBaseView::RemoveSelectedText()
4005 if (m_pViewData == NULL)
4006 return;
4007 if (!HasTextSelection())
4008 return;
4010 // fix selection if starts or ends on empty line
4011 SetCaretViewPosition(m_ptSelectionViewPosEnd);
4012 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4015 m_ptSelectionViewPosEnd = GetCaretViewPosition();
4016 SetCaretViewPosition(m_ptSelectionViewPosStart);
4017 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
4020 m_ptSelectionViewPosStart = GetCaretViewPosition();
4021 if (!HasTextSelection())
4023 ClearSelection();
4024 return;
4027 // We want to undo the insertion in a single step.
4028 ResetUndoStep();
4029 CUndo::GetInstance().BeginGrouping();
4031 // combine first and last line
4032 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
4033 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
4034 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
4035 oFirstLine.ending = oLastLine.ending;
4036 oFirstLine.state = DIFFSTATE_EDITED;
4037 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4039 // clean up middle lines if any
4040 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4042 viewdata oEmptyLine = GetEmptyLineData();
4043 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4045 SetViewData(nViewLine, oEmptyLine);
4047 SaveUndoStep();
4049 if (CleanEmptyLines())
4051 BuildAllScreen2ViewVector(); // schedule full rebuild
4053 SaveUndoStep();
4054 UpdateViewLineNumbers();
4057 SetModified(); //TODO set modified only if real data was changed
4058 SaveUndoStep();
4059 CUndo::GetInstance().EndGrouping();
4061 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4062 SetCaretViewPosition(m_ptSelectionViewPosStart);
4063 UpdateGoalPos();
4064 ClearSelection();
4065 UpdateCaret();
4066 EnsureCaretVisible();
4067 Invalidate(FALSE);
4070 void CBaseView::PasteText()
4072 if (!OpenClipboard())
4073 return;
4075 CString sClipboardText;
4076 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4077 if (hglb)
4079 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4080 sClipboardText = CString(lpstr);
4081 GlobalUnlock(hglb);
4083 hglb = GetClipboardData(CF_UNICODETEXT);
4084 if (hglb)
4086 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4087 sClipboardText = lpstr;
4088 GlobalUnlock(hglb);
4090 CloseClipboard();
4092 if (sClipboardText.IsEmpty())
4093 return;
4095 sClipboardText.Replace(L"\r\n", L"\r");
4096 sClipboardText.Replace('\n', '\r');
4098 InsertText(sClipboardText);
4101 void CBaseView::OnCaretDown()
4103 POINT ptCaretPos = GetCaretPosition();
4104 int nLine = ptCaretPos.y;
4105 int nNextLine = nLine + 1;
4106 if (nNextLine >= GetLineCount()) // already at last line
4108 return;
4111 POINT ptCaretViewPos = GetCaretViewPosition();
4112 int nViewLine = ptCaretViewPos.y;
4113 int nNextViewLine = GetViewLineForScreen(nNextLine);
4114 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4116 // find next suitable screen line
4117 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4119 nNextLine++;
4120 if (nNextLine >= GetLineCount())
4122 return;
4124 nNextViewLine = GetViewLineForScreen(nNextLine);
4127 ptCaretPos.y = nNextLine;
4128 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4129 SetCaretPosition(ptCaretPos);
4130 OnCaretMove(MOVELEFT);
4131 ShowDiffLines(ptCaretPos.y);
4134 bool CBaseView::MoveCaretLeft()
4136 POINT ptCaretViewPos = GetCaretViewPosition();
4138 //int nViewLine = ptCaretViewPos.y;
4139 if (ptCaretViewPos.x == 0)
4141 int nPrevLine = GetCaretPosition().y;
4142 int nPrevViewLine;
4143 do {
4144 nPrevLine--;
4145 if (nPrevLine < 0)
4147 return false;
4149 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4150 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4151 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4152 ShowDiffLines(nPrevLine);
4154 else
4155 --ptCaretViewPos.x;
4157 SetCaretAndGoalViewPosition(ptCaretViewPos);
4158 return true;
4161 bool CBaseView::MoveCaretRight()
4163 POINT ptCaretViewPos = GetCaretViewPosition();
4165 int nViewLine = ptCaretViewPos.y;
4166 int nViewLineLen = GetViewLineLength(nViewLine);
4167 if (ptCaretViewPos.x >= nViewLineLen)
4169 int nNextLine = GetCaretPosition().y;
4170 int nNextViewLine;
4171 do {
4172 nNextLine++;
4173 if (nNextLine >= GetLineCount())
4175 return false;
4177 nNextViewLine = GetViewLineForScreen(nNextLine);
4178 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4179 ptCaretViewPos.y = nNextViewLine;
4180 ptCaretViewPos.x = 0;
4181 ShowDiffLines(nNextLine);
4183 else
4184 ++ptCaretViewPos.x;
4186 SetCaretAndGoalViewPosition(ptCaretViewPos);
4187 return true;
4190 void CBaseView::UpdateGoalPos()
4192 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4195 void CBaseView::OnCaretLeft()
4197 MoveCaretLeft();
4198 OnCaretMove(MOVELEFT);
4201 void CBaseView::OnCaretRight()
4203 MoveCaretRight();
4204 OnCaretMove(MOVERIGHT);
4207 void CBaseView::OnCaretUp()
4209 POINT ptCaretPos = GetCaretPosition();
4210 int nLine = ptCaretPos.y;
4211 if (nLine <= 0) // already at first line
4213 return;
4215 int nPrevLine = nLine - 1;
4217 POINT ptCaretViewPos = GetCaretViewPosition();
4218 int nViewLine = ptCaretViewPos.y;
4219 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4220 if (nPrevViewLine != nViewLine) // not on same view line
4222 // find previous suitable screen line
4223 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4225 if (nPrevLine <= 0)
4227 return;
4229 nPrevLine--;
4230 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4233 ptCaretPos.y = nPrevLine;
4234 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4235 SetCaretPosition(ptCaretPos);
4236 OnCaretMove(MOVELEFT);
4237 ShowDiffLines(ptCaretPos.y);
4240 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4242 switch (GetCharGroup(ch))
4244 case CHG_CONTROL:
4245 case CHG_WHITESPACE:
4246 case CHG_WORDSEPARATOR:
4247 return true;
4249 return false;
4252 bool CBaseView::IsCaretAtWordBoundary()
4254 POINT ptViewCaret = GetCaretViewPosition();
4255 CString line = GetViewLineChars(ptViewCaret.y);
4256 if (line.IsEmpty())
4257 return false; // no boundary at the empty lines
4258 if (ptViewCaret.x == 0)
4259 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4260 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4261 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4262 return
4263 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4264 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4267 void CBaseView::UpdateViewsCaretPosition()
4269 POINT ptCaretPos = GetCaretPosition();
4270 if (m_pwndBottom && m_pwndBottom!=this)
4271 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4272 if (m_pwndLeft && m_pwndLeft!=this)
4273 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4274 if (m_pwndRight && m_pwndRight!=this)
4275 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4278 void CBaseView::OnCaretWordleft()
4280 MoveCaretWordLeft();
4281 OnCaretMove(MOVELEFT);
4284 void CBaseView::OnCaretWordright()
4286 MoveCaretWordRight();
4287 OnCaretMove(MOVERIGHT);
4290 void CBaseView::MoveCaretWordLeft()
4292 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4297 void CBaseView::MoveCaretWordRight()
4299 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4304 void CBaseView::ClearCurrentSelection()
4306 m_ptSelectionViewPosStart = GetCaretViewPosition();
4307 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4308 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4309 m_nSelViewBlockStart = -1;
4310 m_nSelViewBlockEnd = -1;
4311 Invalidate(FALSE);
4314 void CBaseView::ClearSelection()
4316 if (m_pwndLeft)
4317 m_pwndLeft->ClearCurrentSelection();
4318 if (m_pwndRight)
4319 m_pwndRight->ClearCurrentSelection();
4320 if (m_pwndBottom)
4321 m_pwndBottom->ClearCurrentSelection();
4324 void CBaseView::AdjustSelection(bool bMoveLeft)
4326 POINT ptCaretViewPos = GetCaretViewPosition();
4327 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4329 // select all have been used recently update origin
4330 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4332 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4333 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4335 m_ptSelectionViewPosStart = ptCaretViewPos;
4336 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4338 else
4340 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4341 m_ptSelectionViewPosEnd = ptCaretViewPos;
4344 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4346 Invalidate(FALSE);
4349 void CBaseView::OnEditCut()
4351 if (IsWritable())
4353 OnEditCopy();
4354 RemoveSelectedText();
4358 void CBaseView::OnEditPaste()
4360 if (IsWritable())
4362 CUndo::GetInstance().BeginGrouping();
4363 RemoveSelectedText();
4364 PasteText();
4365 CUndo::GetInstance().EndGrouping();
4369 void CBaseView::DeleteFonts()
4371 for (int i=0; i<fontsCount; i++)
4373 if (m_apFonts[i] != NULL)
4375 m_apFonts[i]->DeleteObject();
4376 delete m_apFonts[i];
4377 m_apFonts[i] = NULL;
4382 void CBaseView::OnCaretMove(bool bMoveLeft)
4384 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4385 OnCaretMove(bMoveLeft, bShift);
4388 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4390 if(isShiftPressed)
4391 AdjustSelection(bMoveLeft);
4392 else
4393 ClearSelection();
4394 EnsureCaretVisible();
4395 UpdateCaret();
4398 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4400 AddCutCopyAndPaste(popup);
4403 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4405 popup.AppendMenu(MF_SEPARATOR, NULL);
4406 CString temp;
4407 temp.LoadString(IDS_EDIT_COPY);
4408 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4409 if (IsWritable())
4411 temp.LoadString(IDS_EDIT_CUT);
4412 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4413 temp.LoadString(IDS_EDIT_PASTE);
4414 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4415 popup.AppendMenu(MF_SEPARATOR, NULL);
4419 void CBaseView::CompensateForKeyboard(CPoint& point)
4421 // if the context menu is invoked through the keyboard, we have to use
4422 // a calculated position on where to anchor the menu on
4423 if (ArePointsSame(point, SetupPoint(-1, -1)))
4425 CRect rect;
4426 GetWindowRect(&rect);
4427 point = rect.CenterPoint();
4431 HICON CBaseView::LoadIcon(WORD iconId)
4433 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4434 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4435 return (HICON)icon;
4438 void CBaseView::ReleaseBitmap()
4440 if (m_pCacheBitmap != NULL)
4442 m_pCacheBitmap->DeleteObject();
4443 delete m_pCacheBitmap;
4444 m_pCacheBitmap = NULL;
4448 void CBaseView::BuildMarkedWordArray()
4450 int lineCount = GetLineCount();
4451 m_arMarkedWordLines.clear();
4452 m_arMarkedWordLines.reserve(lineCount);
4453 bool bDoit = !m_sMarkedWord.IsEmpty();
4454 for (int i = 0; i < lineCount; ++i)
4456 if (bDoit)
4458 CString line = GetLineChars(i);
4460 if (!line.IsEmpty())
4462 int found = 0;
4463 int nMarkStart = -1;
4464 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4466 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4467 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4468 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4469 if (eLeft != eStart)
4471 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4472 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4473 if (eRight != eEnd)
4475 found = 1;
4476 break;
4480 m_arMarkedWordLines.push_back(found);
4482 else
4483 m_arMarkedWordLines.push_back(0);
4485 else
4486 m_arMarkedWordLines.push_back(0);
4490 void CBaseView::BuildFindStringArray()
4492 int lineCount = GetLineCount();
4493 m_arFindStringLines.clear();
4494 m_arFindStringLines.reserve(lineCount);
4495 bool bDoit = !m_sFindText.IsEmpty();
4496 int s = 0;
4497 int e = 0;
4498 for (int i = 0; i < lineCount; ++i)
4500 if (bDoit)
4502 CString line = GetLineChars(i);
4504 if (!line.IsEmpty())
4506 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4508 case DIFFSTATE_EMPTY:
4509 m_arFindStringLines.push_back(0);
4510 break;
4511 case DIFFSTATE_UNKNOWN:
4512 case DIFFSTATE_NORMAL:
4513 if (m_bLimitToDiff)
4515 m_arFindStringLines.push_back(0);
4516 break;
4518 case DIFFSTATE_REMOVED:
4519 case DIFFSTATE_REMOVEDWHITESPACE:
4520 case DIFFSTATE_ADDED:
4521 case DIFFSTATE_ADDEDWHITESPACE:
4522 case DIFFSTATE_WHITESPACE:
4523 case DIFFSTATE_WHITESPACE_DIFF:
4524 case DIFFSTATE_CONFLICTED:
4525 case DIFFSTATE_CONFLICTED_IGNORED:
4526 case DIFFSTATE_CONFLICTADDED:
4527 case DIFFSTATE_CONFLICTEMPTY:
4528 case DIFFSTATE_CONFLICTRESOLVED:
4529 case DIFFSTATE_IDENTICALREMOVED:
4530 case DIFFSTATE_IDENTICALADDED:
4531 case DIFFSTATE_THEIRSREMOVED:
4532 case DIFFSTATE_THEIRSADDED:
4533 case DIFFSTATE_YOURSREMOVED:
4534 case DIFFSTATE_YOURSADDED:
4535 case DIFFSTATE_EDITED:
4537 if (!m_bMatchCase)
4538 line = line.MakeLower();
4539 s = 0;
4540 e = 0;
4541 int match = 0;
4542 while (StringFound(line, SearchNext, s, e))
4544 match++;
4545 s = e;
4546 e = 0;
4548 m_arFindStringLines.push_back(match);
4549 break;
4551 default:
4552 m_arFindStringLines.push_back(0);
4555 else
4556 m_arFindStringLines.push_back(0);
4558 else
4559 m_arFindStringLines.push_back(0);
4561 UpdateLocator();
4564 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4566 if (!m_bShowInlineDiff)
4567 return false;
4568 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4569 return false;
4571 if (m_pViewData == nullptr || m_pViewData->GetCount() <= nViewLine)
4572 return false;
4573 const CString &sLine = m_pViewData->GetLine(nViewLine);
4574 if (sLine.IsEmpty())
4575 return false;
4577 CheckOtherView();
4578 if (!m_pOtherViewData)
4579 return false;
4581 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4582 if (sDiffLine.IsEmpty())
4583 return false;
4585 svn_diff_t * diff = NULL;
4586 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4587 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4588 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4589 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4590 return false;
4592 size_t lineoffset = 0;
4593 size_t position = 0;
4594 while (diff)
4596 if (this == m_pwndRight)
4598 apr_off_t nTmp = diff->modified_length;
4599 diff->modified_length = diff->original_length;
4600 diff->original_length = nTmp;
4602 nTmp = diff->modified_start;
4603 diff->modified_start = diff->original_start;
4604 diff->original_start = nTmp;
4606 apr_off_t len = diff->original_length;
4607 size_t oldpos = position;
4609 for (apr_off_t i = 0; i < len; ++i)
4611 position += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4612 lineoffset++;
4615 if (diff->type == svn_diff__type_diff_modified)
4617 inlineDiffPos p;
4618 p.start = oldpos;
4619 p.end = position;
4620 positions.push_back(p);
4623 diff = diff->next;
4626 return !positions.empty();
4629 void CBaseView::OnNavigateNextinlinediff()
4631 int nX;
4632 if (GetNextInlineDiff(nX))
4634 POINT ptCaretViewPos = GetCaretViewPosition();
4635 ptCaretViewPos.x = nX;
4636 SetCaretAndGoalViewPosition(ptCaretViewPos);
4637 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4638 EnsureCaretVisible();
4642 void CBaseView::OnNavigatePrevinlinediff()
4644 int nX;
4645 if (GetPrevInlineDiff(nX))
4647 POINT ptCaretViewPos = GetCaretViewPosition();
4648 ptCaretViewPos.x = nX;
4649 SetCaretAndGoalViewPosition(ptCaretViewPos);
4650 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4651 EnsureCaretVisible();
4655 bool CBaseView::HasNextInlineDiff()
4657 int nPos;
4658 return GetNextInlineDiff(nPos);
4661 bool CBaseView::GetNextInlineDiff(int & nPos)
4663 POINT ptCaretViewPos = GetCaretViewPosition();
4664 std::vector<inlineDiffPos> positions;
4665 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4667 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4669 if (it->start > ptCaretViewPos.x)
4671 nPos = (LONG)it->start;
4672 return true;
4674 if (it->end > ptCaretViewPos.x)
4676 nPos = (LONG)it->end;
4677 return true;
4681 return false;
4684 bool CBaseView::HasPrevInlineDiff()
4686 int nPos;
4687 return GetPrevInlineDiff(nPos);
4690 bool CBaseView::GetPrevInlineDiff(int & nPos)
4692 POINT ptCaretViewPos = GetCaretViewPosition();
4693 std::vector<inlineDiffPos> positions;
4694 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4696 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4698 if ( it->end < ptCaretViewPos.x)
4700 nPos = (LONG)it->end;
4701 return true;
4703 if ( it->start < ptCaretViewPos.x)
4705 nPos = (LONG)it->start;
4706 return true;
4710 return false;
4713 CBaseView * CBaseView::GetFirstGoodView()
4715 if (IsViewGood(m_pwndLeft))
4716 return m_pwndLeft;
4717 if (IsViewGood(m_pwndRight))
4718 return m_pwndRight;
4719 if (IsViewGood(m_pwndBottom))
4720 return m_pwndBottom;
4721 return NULL;
4724 void CBaseView::BuildAllScreen2ViewVector()
4726 CBaseView * p_pwndView = GetFirstGoodView();
4727 if (p_pwndView)
4729 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4733 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4735 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4738 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4740 CBaseView * p_pwndView = GetFirstGoodView();
4741 if (p_pwndView)
4743 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4747 void CBaseView::UpdateViewLineNumbers()
4749 int nLineNumber = 0;
4750 int nViewLineCount = GetViewCount();
4751 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4753 int oldLine = (int)GetViewLineNumber(nViewLine);
4754 if (oldLine >= 0)
4755 SetViewLineNumber(nViewLine, nLineNumber++);
4757 m_nDigits = 0;
4760 int CBaseView::CleanEmptyLines()
4762 int nRemovedCount = 0;
4763 int nViewLineCount = GetViewCount();
4764 bool bCheckLeft = IsViewGood(m_pwndLeft);
4765 bool bCheckRight = IsViewGood(m_pwndRight);
4766 bool bCheckBottom = IsViewGood(m_pwndBottom);
4767 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4769 bool bAllEmpty = true;
4770 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4771 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4772 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4773 if (bAllEmpty)
4775 if (bCheckLeft)
4777 m_pwndLeft->RemoveViewData(nViewLine);
4779 if (bCheckRight)
4781 m_pwndRight->RemoveViewData(nViewLine);
4783 if (bCheckBottom)
4785 m_pwndBottom->RemoveViewData(nViewLine);
4787 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4789 SaveUndoStep();
4791 nViewLineCount--;
4792 nRemovedCount++;
4793 continue;
4795 nViewLine++;
4797 return nRemovedCount;
4800 int CBaseView::FindScreenLineForViewLine( int viewLine )
4802 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4805 int CBaseView::CountMultiLines( int nViewLine )
4807 if (m_ScreenedViewLine.empty())
4808 return 0; // in case the view is completely empty
4810 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4812 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4814 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4817 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4819 TScreenedViewLine oScreenedLine;
4820 // tokenize string
4821 int prevpos = 0;
4822 int pos = 0;
4823 while ((pos = multiline.Find('\n', pos)) >= 0)
4825 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4826 pos++;
4827 prevpos = pos;
4829 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4830 oScreenedLine.bSublinesSet = true;
4831 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4833 return CountMultiLines(nViewLine);
4836 /// prepare inline diff cache
4837 LineColors & CBaseView::GetLineColors(int nViewLine)
4839 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4841 if (m_bWhitespaceInlineDiffs)
4843 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4844 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4846 else
4848 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4849 return m_ScreenedViewLine[nViewLine].lineColors;
4852 LineColors oLineColors;
4853 // set main line color
4854 COLORREF crBkgnd, crText;
4855 DiffStates diffState = m_pViewData->GetState(nViewLine);
4856 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4857 oLineColors.SetColor(0, crText, crBkgnd);
4859 do {
4860 if (!m_bShowInlineDiff)
4861 break;
4863 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4864 break;
4866 CString sLine = GetViewLineChars(nViewLine);
4867 if (sLine.IsEmpty())
4868 break;
4869 if (!m_pOtherView)
4870 break;
4872 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4873 if (sDiffLine.IsEmpty())
4874 break;
4876 svn_diff_t * diff = NULL;
4877 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4878 break;
4879 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4880 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4881 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4882 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4883 break;
4885 int lineoffset = 0;
4886 int nTextStartOffset = 0;
4887 std::map<int, COLORREF> removedPositions;
4888 while (diff)
4890 if (this == m_pwndRight)
4892 apr_off_t nTmp = diff->modified_length;
4893 diff->modified_length = diff->original_length;
4894 diff->original_length = nTmp;
4896 nTmp = diff->modified_start;
4897 diff->modified_start = diff->original_start;
4898 diff->original_start = nTmp;
4900 apr_off_t len = diff->original_length;
4902 size_t nTextLength = 0;
4903 for (int i = 0; i < len; ++i)
4905 nTextLength += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4906 lineoffset++;
4908 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4910 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4911 if ((m_bShowInlineDiff)&&(bInlineDiff))
4913 crBkgnd = InlineViewLineDiffColor(nViewLine);
4915 else
4917 crBkgnd = m_ModifiedBk;
4920 if (len < diff->modified_length)
4922 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4924 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4926 nTextStartOffset += (int)nTextLength;
4927 diff = diff->next;
4929 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4931 oLineColors.AddShotColor(it->first, it->second);
4933 } while (false); // error catch
4935 if (!m_bWhitespaceInlineDiffs)
4937 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4938 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4940 else
4942 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4943 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4946 return GetLineColors(nViewLine);
4949 void CBaseView::OnEditSelectall()
4951 if (m_pViewData == nullptr)
4952 return;
4953 int nLastViewLine = m_pViewData->GetCount()-1;
4954 if (nLastViewLine < 0)
4955 return;
4956 SetupAllViewSelection(0, nLastViewLine);
4958 CString sLine = GetViewLineChars(nLastViewLine);
4959 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4960 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4961 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4963 UpdateWindow();
4966 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4968 FilterWhitespaces(first);
4969 FilterWhitespaces(second);
4972 void CBaseView::FilterWhitespaces(CString& line)
4974 line.Remove(' ');
4975 line.Remove('\t');
4976 line.Remove('\r');
4977 line.Remove('\n');
4980 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4982 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4983 int nEventLine = nLineFromTop + m_nTopLine;
4984 nEventLine--; //we need the index
4985 return nEventLine;
4989 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4991 if (RelayTrippleClick(pMsg))
4992 return TRUE;
4993 return CView::PreTranslateMessage(pMsg);
4997 void CBaseView::ResetUndoStep()
4999 m_AllState.Clear();
5002 void CBaseView::SaveUndoStep()
5004 if (!m_AllState.IsEmpty())
5006 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
5008 ResetUndoStep();
5011 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
5013 m_pState->addedlines.push_back(index);
5014 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
5017 void CBaseView::InsertViewData( int index, const viewdata& data )
5019 m_pState->addedlines.push_back(index);
5020 m_pViewData->InsertData(index, data);
5023 void CBaseView::RemoveViewData( int index )
5025 m_pState->removedlines[index] = m_pViewData->GetData(index);
5026 m_pViewData->RemoveData(index);
5029 void CBaseView::SetViewData( int index, const viewdata& data )
5031 m_pState->replacedlines[index] = m_pViewData->GetData(index);
5032 m_pViewData->SetData(index, data);
5035 void CBaseView::SetViewState( int index, DiffStates state )
5037 m_pState->linestates[index] = m_pViewData->GetState(index);
5038 m_pViewData->SetState(index, state);
5041 void CBaseView::SetViewLine( int index, const CString& sLine )
5043 m_pState->difflines[index] = m_pViewData->GetLine(index);
5044 m_pViewData->SetLine(index, sLine);
5047 void CBaseView::SetViewLineNumber( int index, int linenumber )
5049 int oldLineNumber = m_pViewData->GetLineNumber(index);
5050 if (oldLineNumber != linenumber) {
5051 m_pState->linelines[index] = oldLineNumber;
5052 m_pViewData->SetLineNumber(index, linenumber);
5056 void CBaseView::SetViewLineEnding( int index, EOL ending )
5058 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
5059 m_pViewData->SetLineEnding(index, ending);
5062 void CBaseView::SetViewMarked( int index, bool marked )
5064 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5065 m_pViewData->SetMarked(index, marked);
5069 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5071 if (HasSelection())
5073 start = m_nSelViewBlockStart;
5074 end = m_nSelViewBlockEnd;
5075 return true;
5077 return false;
5080 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5082 RebuildIfNecessary();
5083 if ((size() <= screenLine) || (screenLine < 0))
5084 return 0;
5085 return m_Screen2View[screenLine].nViewLine;
5088 int CBaseView::Screen2View::size()
5090 RebuildIfNecessary();
5091 return (int)m_Screen2View.size();
5094 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5096 RebuildIfNecessary();
5097 if (size() <= screenLine)
5098 return 0;
5099 return m_Screen2View[screenLine].nViewSubLine;
5102 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5104 RebuildIfNecessary();
5105 return m_Screen2View[screenLine];
5109 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5111 void CBaseView::Screen2View::RebuildIfNecessary()
5113 if (!m_pViewData)
5114 return; // rebuild not necessary
5116 FixScreenedCacheSize(m_pwndLeft);
5117 FixScreenedCacheSize(m_pwndRight);
5118 FixScreenedCacheSize(m_pwndBottom);
5119 if (!m_bFull)
5121 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5123 ResetScreenedViewLineCache(m_pwndLeft, *it);
5124 ResetScreenedViewLineCache(m_pwndRight, *it);
5125 ResetScreenedViewLineCache(m_pwndBottom, *it);
5128 else
5130 ResetScreenedViewLineCache(m_pwndLeft);
5131 ResetScreenedViewLineCache(m_pwndRight);
5132 ResetScreenedViewLineCache(m_pwndBottom);
5134 m_RebuildRanges.clear();
5135 m_bFull = false;
5137 size_t OldSize = m_Screen2View.size();
5138 m_Screen2View.clear();
5139 m_Screen2View.reserve(OldSize); // guess same size
5140 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5142 if (m_pMainFrame->m_bCollapsed)
5144 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5145 ++i;
5146 if (!(i < m_pViewData->GetCount()))
5147 break;
5149 TScreenLineInfo oLineInfo;
5150 oLineInfo.nViewLine = i;
5151 oLineInfo.nViewSubLine = -1; // no wrap
5152 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5154 int nMaxLines = 0;
5155 if (IsLeftViewGood())
5156 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5157 if (IsRightViewGood())
5158 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5159 if (IsBottomViewGood())
5160 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5161 for (int l = 0; l < (nMaxLines-1); ++l)
5163 oLineInfo.nViewSubLine++;
5164 m_Screen2View.push_back(oLineInfo);
5166 oLineInfo.nViewSubLine++;
5168 m_Screen2View.push_back(oLineInfo);
5170 m_pViewData = NULL;
5172 if (IsLeftViewGood())
5173 m_pwndLeft->BuildMarkedWordArray();
5174 if (IsRightViewGood())
5175 m_pwndRight->BuildMarkedWordArray();
5176 if (IsBottomViewGood())
5177 m_pwndBottom->BuildMarkedWordArray();
5178 UpdateLocator();
5179 RecalcAllVertScrollBars();
5180 RecalcAllHorzScrollBars();
5183 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5185 RebuildIfNecessary();
5187 int nScreenLineCount = (int)m_Screen2View.size();
5189 int nPos = 0;
5190 if (nScreenLineCount>16)
5192 // for enough long data search for last screen
5193 // with viewline less than one we are looking for
5194 // use approximate method (based on) binary search using asymmetric start point
5195 // in form 2**n (determined as MSB of length) to go around division and rounding;
5196 // this effectively looks for bit values from MSB to LSB
5198 int nTestBit;
5199 //GetMostSignificantBitValue
5200 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5201 nTestBit = nScreenLineCount;
5202 nTestBit |= nTestBit>>1;
5203 nTestBit |= nTestBit>>2;
5204 nTestBit |= nTestBit>>4;
5205 nTestBit |= nTestBit>>8;
5206 nTestBit |= nTestBit>>16;
5207 nTestBit ^= (nTestBit>>1);
5209 while (nTestBit)
5211 int nTestPos = nPos | nTestBit;
5212 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5214 nPos = nTestPos;
5216 nTestBit >>= 1;
5219 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5221 nPos++;
5224 return nPos;
5227 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5228 m_bFull = true;
5230 m_pViewData = pViewData;
5233 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5235 if (m_bFull)
5236 return;
5238 m_pViewData = pViewData;
5240 TRebuildRange Range;
5241 Range.FirstViewLine=nFirstViewLine;
5242 Range.LastViewLine=nLastViewLine;
5243 m_RebuildRanges.push_back(Range);
5246 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5248 if (!IsViewGood(pwndView))
5250 return false;
5252 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5253 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5254 if (nOldSize == nViewCount)
5256 return false;
5258 pwndView->m_ScreenedViewLine.resize(nViewCount);
5259 return true;
5262 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5264 if (!IsViewGood(pwndView))
5266 return false;
5268 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5269 ResetScreenedViewLineCache(pwndView, Range);
5270 return true;
5273 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5275 if (!IsViewGood(pwndView))
5277 return false;
5279 if (Range.LastViewLine == -1)
5281 return false;
5283 ASSERT(Range.FirstViewLine >= 0);
5284 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5285 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5287 pwndView->m_ScreenedViewLine[i].Clear();
5289 return false;
5292 void CBaseView::WrapChanged()
5294 m_nMaxLineLength = -1;
5295 m_nOffsetChar = 0;
5298 void CBaseView::OnEditFind()
5300 if (m_pFindDialog)
5301 return;
5303 int id = 0;
5304 if (this == m_pwndLeft)
5305 id = 1;
5306 if (this == m_pwndRight)
5307 id = 2;
5308 if (this == m_pwndBottom)
5309 id = 3;
5311 m_pFindDialog = new CFindDlg(this);
5312 m_pFindDialog->Create(this, id);
5314 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5315 m_pFindDialog->SetReadonly(m_bReadonly);
5318 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5320 ASSERT(m_pFindDialog != NULL);
5322 if (m_pFindDialog->IsTerminating())
5324 // invalidate the handle identifying the dialog box.
5325 m_pFindDialog = NULL;
5326 return 0;
5329 if(m_pFindDialog->FindNext())
5331 //read data from dialog
5332 m_sFindText = m_pFindDialog->GetFindString();
5333 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5334 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5335 m_bWholeWord = m_pFindDialog->WholeWord();
5337 if (!m_bMatchCase)
5338 m_sFindText = m_sFindText.MakeLower();
5340 BuildFindStringArray();
5341 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5343 if (m_pFindDialog->SearchUp())
5344 OnEditFindprev();
5345 else
5346 OnEditFindnext();
5348 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5350 size_t count = 0;
5351 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5352 count += m_arFindStringLines[i];
5353 CString format;
5354 format.LoadString(IDS_FIND_COUNT);
5355 CString matches;
5356 matches.Format(format, count);
5357 m_pFindDialog->SetStatusText(matches);
5359 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5361 if (!IsWritable())
5362 return 0;
5363 bool bFound = false;
5364 if (m_pFindDialog->SearchUp())
5365 bFound = Search(SearchPrevious, true, true, false);
5366 else
5367 bFound = Search(SearchNext, true, true, false);
5368 if (bFound)
5370 CString sReplaceText = m_pFindDialog->GetReplaceString();
5371 CUndo::GetInstance().BeginGrouping();
5372 RemoveSelectedText();
5373 InsertText(sReplaceText);
5374 CUndo::GetInstance().EndGrouping();
5378 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5380 if (!IsWritable())
5381 return 0;
5382 bool bFound = false;
5383 int replaceCount = 0;
5384 POINT lastPoint = m_ptSelectionViewPosStart;
5385 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5386 CUndo::GetInstance().BeginGrouping();
5389 bFound = Search(SearchNext, true, false, true);
5390 if (bFound)
5392 CString sReplaceText = m_pFindDialog->GetReplaceString();
5393 RemoveSelectedText();
5394 InsertText(sReplaceText);
5395 ++replaceCount;
5397 } while (bFound);
5398 CUndo::GetInstance().EndGrouping();
5399 if (replaceCount == 0)
5400 m_ptSelectionViewPosStart = lastPoint;
5401 CString message;
5402 message.Format(IDS_FIND_REPLACED, replaceCount);
5403 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5407 return 0;
5410 void CBaseView::OnEditFindnextStart()
5412 if (m_pViewData == nullptr)
5413 return;
5414 if (HasTextSelection())
5416 m_sFindText = GetSelectedText();
5417 m_bMatchCase = false;
5418 m_bLimitToDiff = false;
5419 m_bWholeWord = false;
5420 m_sFindText = m_sFindText.MakeLower();
5422 BuildFindStringArray();
5423 OnEditFindnext();
5425 else
5427 m_sFindText.Empty();
5428 BuildFindStringArray();
5432 void CBaseView::OnEditFindprevStart()
5434 if (m_pViewData == nullptr)
5435 return;
5436 if (HasTextSelection())
5438 m_sFindText = GetSelectedText();
5439 m_bMatchCase = false;
5440 m_bLimitToDiff = false;
5441 m_bWholeWord = false;
5442 m_sFindText = m_sFindText.MakeLower();
5444 BuildFindStringArray();
5445 OnEditFindprev();
5447 else
5449 m_sFindText.Empty();
5450 BuildFindStringArray();
5454 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5456 if (srchDir == SearchPrevious)
5458 int laststart = -1;
5459 int laststart2 = -1;
5462 laststart2 = laststart;
5463 laststart = str.Find(m_sFindText, laststart + 1);
5464 } while (laststart >= 0 && laststart < start);
5465 start = laststart2;
5467 else
5468 start = str.Find(m_sFindText, start);
5469 end = start + m_sFindText.GetLength();
5470 bool bStringFound = (start >= 0);
5471 if (bStringFound && m_bWholeWord)
5473 if (start)
5474 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5476 if (bStringFound)
5478 if (str.GetLength() > end)
5479 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5482 return bStringFound;
5485 void CBaseView::OnEditFindprev()
5487 Search(SearchPrevious, false, true, false);
5490 void CBaseView::OnEditFindnext()
5492 Search(SearchNext, false, true, false);
5495 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5497 if (m_sFindText.IsEmpty())
5498 return false;
5499 if(!m_pViewData)
5500 return false;
5502 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5503 POINT end;
5504 end.y = m_pViewData->GetCount()-1;
5505 if (end.y < 0)
5506 return false;
5508 if (srchDir==SearchNext)
5509 end.x = GetViewLineLength(end.y);
5510 else
5512 end.x = m_ptSelectionViewPosStart.x;
5513 start.x = 0;
5516 if (!HasTextSelection())
5518 start.y = m_ptCaretViewPos.y;
5519 if (srchDir==SearchNext)
5520 start.x = m_ptCaretViewPos.x;
5521 else
5523 start.x = 0;
5524 end.x = m_ptCaretViewPos.x;
5527 CString sSelectedText;
5528 int startline = -1;
5529 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5531 if (nViewLine < 0)
5533 if (stopEof)
5534 return false;
5535 nViewLine = m_pViewData->GetCount()-1;
5536 startline = start.y;
5537 if (flashIfNotFound)
5539 if (m_pFindDialog)
5540 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5541 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5544 if (nViewLine > end.y)
5546 if (stopEof)
5547 return false;
5548 nViewLine = 0;
5549 startline = start.y;
5550 if (flashIfNotFound)
5552 if (m_pFindDialog)
5553 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5554 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5557 switch (m_pViewData->GetState(nViewLine))
5559 case DIFFSTATE_EMPTY:
5560 break;
5561 case DIFFSTATE_UNKNOWN:
5562 case DIFFSTATE_NORMAL:
5563 if (m_bLimitToDiff)
5564 break;
5565 case DIFFSTATE_REMOVED:
5566 case DIFFSTATE_REMOVEDWHITESPACE:
5567 case DIFFSTATE_ADDED:
5568 case DIFFSTATE_ADDEDWHITESPACE:
5569 case DIFFSTATE_WHITESPACE:
5570 case DIFFSTATE_WHITESPACE_DIFF:
5571 case DIFFSTATE_CONFLICTED:
5572 case DIFFSTATE_CONFLICTED_IGNORED:
5573 case DIFFSTATE_CONFLICTADDED:
5574 case DIFFSTATE_CONFLICTEMPTY:
5575 case DIFFSTATE_CONFLICTRESOLVED:
5576 case DIFFSTATE_IDENTICALREMOVED:
5577 case DIFFSTATE_IDENTICALADDED:
5578 case DIFFSTATE_THEIRSREMOVED:
5579 case DIFFSTATE_THEIRSADDED:
5580 case DIFFSTATE_YOURSREMOVED:
5581 case DIFFSTATE_YOURSADDED:
5582 case DIFFSTATE_EDITED:
5584 sSelectedText = GetViewLineChars(nViewLine);
5585 if (nViewLine == start.y && startline < 0)
5586 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5587 if (!m_bMatchCase)
5588 sSelectedText = sSelectedText.MakeLower();
5589 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5590 int endfound = 0;
5591 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5593 HighlightViewLines(nViewLine, nViewLine);
5594 m_ptSelectionViewPosStart.x = startfound;
5595 m_ptSelectionViewPosEnd.x = endfound;
5596 if (nViewLine == start.y && startline < 0)
5598 m_ptSelectionViewPosStart.x += start.x;
5599 m_ptSelectionViewPosEnd.x += start.x;
5601 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5602 m_ptSelectionViewPosStart.y = nViewLine;
5603 m_ptSelectionViewPosEnd.y = nViewLine;
5604 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5605 UpdateViewsCaretPosition();
5606 EnsureCaretVisible();
5607 Invalidate();
5608 return true;
5611 break;
5614 if (startline >= 0)
5616 if (nViewLine == startline)
5618 if (flashIfNotFound)
5620 CString message;
5621 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5622 if (m_pFindDialog)
5623 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5624 ::MessageBeep(0xFFFFFFFF);
5625 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5627 break;
5631 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5632 return false;
5635 CString CBaseView::GetSelectedText() const
5637 CString sSelectedText;
5638 POINT start = m_ptSelectionViewPosStart;
5639 POINT end = m_ptSelectionViewPosEnd;
5640 if (!HasTextSelection())
5642 if (!HasSelection())
5643 return sSelectedText;
5644 start.y = m_nSelViewBlockStart;
5645 start.x = 0;
5646 end.y = m_nSelViewBlockEnd;
5647 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5649 if (m_pViewData == nullptr)
5650 return sSelectedText;
5651 // first store the selected lines in one CString
5652 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5654 switch (m_pViewData->GetState(nViewLine))
5656 case DIFFSTATE_EMPTY:
5657 break;
5658 case DIFFSTATE_UNKNOWN:
5659 case DIFFSTATE_NORMAL:
5660 case DIFFSTATE_REMOVED:
5661 case DIFFSTATE_REMOVEDWHITESPACE:
5662 case DIFFSTATE_ADDED:
5663 case DIFFSTATE_ADDEDWHITESPACE:
5664 case DIFFSTATE_WHITESPACE:
5665 case DIFFSTATE_WHITESPACE_DIFF:
5666 case DIFFSTATE_CONFLICTED:
5667 case DIFFSTATE_CONFLICTED_IGNORED:
5668 case DIFFSTATE_CONFLICTADDED:
5669 case DIFFSTATE_CONFLICTEMPTY:
5670 case DIFFSTATE_CONFLICTRESOLVED:
5671 case DIFFSTATE_IDENTICALREMOVED:
5672 case DIFFSTATE_IDENTICALADDED:
5673 case DIFFSTATE_THEIRSREMOVED:
5674 case DIFFSTATE_THEIRSADDED:
5675 case DIFFSTATE_YOURSREMOVED:
5676 case DIFFSTATE_YOURSADDED:
5677 case DIFFSTATE_EDITED:
5678 sSelectedText += GetViewLineChars(nViewLine);
5679 sSelectedText += L"\r\n";
5680 break;
5683 // remove the non-selected chars from the first line, last line and last \r\n
5684 int nLeftCut = start.x;
5685 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5686 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5687 return sSelectedText;
5690 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5692 hasMods = false;
5693 hasConflicts = false;
5694 hasWhitespaceMods = false;
5696 if (m_pViewData)
5698 for (int i=0; i<m_pViewData->GetCount(); i++)
5700 DiffStates state = m_pViewData->GetState(i);
5701 switch (state)
5703 case DIFFSTATE_ADDED:
5704 case DIFFSTATE_IDENTICALADDED:
5705 case DIFFSTATE_THEIRSADDED:
5706 case DIFFSTATE_YOURSADDED:
5707 case DIFFSTATE_CONFLICTADDED:
5708 case DIFFSTATE_IDENTICALREMOVED:
5709 case DIFFSTATE_REMOVED:
5710 case DIFFSTATE_THEIRSREMOVED:
5711 case DIFFSTATE_YOURSREMOVED:
5712 case DIFFSTATE_EMPTY:
5713 hasMods = true;
5714 break;
5715 case DIFFSTATE_CONFLICTED:
5716 case DIFFSTATE_CONFLICTED_IGNORED:
5717 hasConflicts = true;
5718 break;
5719 case DIFFSTATE_REMOVEDWHITESPACE:
5720 case DIFFSTATE_ADDEDWHITESPACE:
5721 case DIFFSTATE_WHITESPACE:
5722 case DIFFSTATE_WHITESPACE_DIFF:
5723 hasWhitespaceMods = true;
5724 break;
5730 void CBaseView::OnEditGotoline()
5732 if (m_pViewData == NULL)
5733 return;
5734 // find the last and first line number
5735 int nViewLineCount = m_pViewData->GetCount();
5737 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5738 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5740 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5741 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5743 break;
5746 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5748 return;
5750 nLastLineNumber++;
5751 int nFirstLineNumber=1; // first is always 1
5753 CString sText;
5754 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5756 CGotoLineDlg dlg(this);
5757 dlg.SetLabel(sText);
5758 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5759 if (dlg.DoModal() == IDOK)
5761 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5763 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5765 HighlightViewLines(nViewLine, nViewLine);
5766 return;
5772 void CBaseView::OnToggleReadonly()
5774 if (IsReadonlyChangable()) {
5775 SetWritable(IsReadonly());
5779 int CBaseView::SaveFile(int nFlags)
5781 Invalidate();
5782 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5784 CFileTextLines file;
5785 m_SaveParams.m_LineEndings = m_lineendings;
5786 m_SaveParams.m_UnicodeType = m_texttype;
5787 file.SetSaveParams(m_SaveParams);
5789 for (int i=0; i<m_pViewData->GetCount(); i++)
5791 //only copy non-removed lines
5792 DiffStates state = m_pViewData->GetState(i);
5793 switch (state)
5795 case DIFFSTATE_CONFLICTED:
5796 case DIFFSTATE_CONFLICTED_IGNORED:
5798 int first = i;
5799 int last = i;
5802 last++;
5803 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5804 file.Add(L"<<<<<<< .mine", EOL_NOENDING);
5805 for (int j=first; j<last; j++)
5807 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5809 file.Add(L"=======", EOL_NOENDING);
5810 for (int j=first; j<last; j++)
5812 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5814 file.Add(L">>>>>>> .theirs", EOL_NOENDING);
5815 i = last-1;
5817 break;
5818 case DIFFSTATE_EMPTY:
5819 break;
5820 case DIFFSTATE_CONFLICTEMPTY:
5821 case DIFFSTATE_IDENTICALREMOVED:
5822 case DIFFSTATE_REMOVED:
5823 case DIFFSTATE_THEIRSREMOVED:
5824 case DIFFSTATE_YOURSREMOVED:
5825 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5826 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5828 // do not save removed lines
5829 break;
5831 default:
5832 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5833 break;
5836 CString filename = m_pWorkingFile->GetFilename();
5837 if (m_pWorkingFile->IsReadonly())
5838 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5839 return -1;
5840 if (!file.Save(filename))
5842 ::MessageBox(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
5843 return -1;
5845 m_pWorkingFile->SetFileName(filename);
5846 m_pWorkingFile->StoreFileAttributes();
5847 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5848 SetModified(FALSE);
5849 CUndo::GetInstance().MarkAsOriginalState(
5850 this == m_pwndLeft,
5851 this == m_pwndRight,
5852 this == m_pwndBottom);
5853 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5854 return 0;
5855 return file.GetCount();
5857 return 1;
5861 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5863 if (m_pWorkingFile)
5865 m_pWorkingFile->SetFileName(sFileName);
5866 return SaveFile(nFlags);
5868 return -1;
5872 EOL CBaseView::GetLineEndings()
5874 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5877 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5879 if (bHasMixedEols)
5881 return EOL_AUTOLINE; // mixed eols - hack value
5883 if (m_lineendings == EOL_AUTOLINE)
5885 return EOL_CRLF;
5887 return m_lineendings;
5890 void CBaseView::ReplaceLineEndings(EOL eEol)
5892 if (eEol == EOL_AUTOLINE)
5894 return;
5896 // set AUTOLINE
5897 m_lineendings = eEol;
5898 // replace all set EOLs
5899 // TODO store line endings and lineendings in undo
5900 //CUndo::BeginGrouping();
5901 for (int i = 0; i < GetViewCount(); ++i)
5903 if (IsLineEmpty(i))
5905 continue;
5907 EOL eLineEol = GetViewLineEnding(i);
5908 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5910 continue;
5912 SetViewLineEnding(i, eEol);
5914 //CUndo::EndGrouping();
5915 //CUndo::saveundostep;
5916 DocumentUpdated();
5917 SetModified();
5920 void CBaseView::SetLineEndingStyle(EOL eEol)
5922 m_lineendings = eEol;
5925 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5927 if (m_texttype == eTextType)
5929 return;
5931 m_texttype = eTextType;
5932 DocumentUpdated();
5933 SetModified();
5936 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5938 if (IsReadonly())
5939 return; // nothing to be changed in read-only view
5940 CEncodingDlg dlg;
5941 dlg.view.LoadString(nTextId);
5942 dlg.texttype = m_texttype;
5943 dlg.lineendings = GetLineEndings();
5944 if (dlg.DoModal() != IDOK)
5945 return;
5946 SetTextType(dlg.texttype);
5947 ReplaceLineEndings(dlg.lineendings);
5951 Replaces lines from source view to this
5953 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
5955 if (!IsViewGood(pwndView))
5956 return;
5957 if (!IsWritable())
5958 return;
5959 CUndo::GetInstance().BeginGrouping();
5961 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5963 bool skip = fnSkip(viewLine);
5964 if (skip)
5966 if (GetViewMarked(viewLine))
5967 SetViewMarked(viewLine, false);
5968 continue;
5970 viewdata line = pwndView->GetViewData(viewLine);
5971 if (line.ending != EOL_NOENDING)
5972 line.ending = m_lineendings;
5973 switch (line.state)
5975 case DIFFSTATE_CONFLICTEMPTY:
5976 case DIFFSTATE_UNKNOWN:
5977 line.state = DIFFSTATE_EMPTY;
5978 case DIFFSTATE_EMPTY:
5979 break;
5980 case DIFFSTATE_ADDED:
5981 case DIFFSTATE_CONFLICTADDED:
5982 case DIFFSTATE_CONFLICTED:
5983 case DIFFSTATE_CONFLICTED_IGNORED:
5984 case DIFFSTATE_IDENTICALADDED:
5985 case DIFFSTATE_THEIRSADDED:
5986 case DIFFSTATE_YOURSADDED:
5987 case DIFFSTATE_IDENTICALREMOVED:
5988 case DIFFSTATE_REMOVED:
5989 case DIFFSTATE_THEIRSREMOVED:
5990 case DIFFSTATE_YOURSREMOVED:
5991 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5992 line.state = DIFFSTATE_NORMAL;
5993 case DIFFSTATE_NORMAL:
5994 break;
5995 default:
5996 break;
5998 bool marked = GetViewMarked(viewLine);
5999 SetViewData(viewLine, line);
6000 if (marked)
6001 SetViewMarked(viewLine, false);
6002 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
6004 // if this view is in ASCII and the other is not, we have to make sure that
6005 // the text we copy from the other view can actually be saved in ASCII encoding.
6006 // if not, we have to change this views encoding to the same encoding as the other view
6007 BOOL useDefault = FALSE;
6008 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
6009 if (useDefault) // a default char is required, so the char can not be saved as ASCII
6010 SetTextType(pwndView->GetTextType());
6013 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6014 // TODO: check if copied line is same as original one set modified only when differ
6015 SetModified();
6016 SaveUndoStep();
6018 int nRemovedLines = CleanEmptyLines();
6019 SaveUndoStep();
6020 //VerifyEols();
6021 // make sure all non empty line have EOL set but last
6022 // wrong can be last copied line(have eol, but no line under),
6023 // or old last line (line before copied block missing eol, but have line under)
6024 // we'll check all lines to be sure
6025 int nLine = GetViewCount();
6026 // check last line have no EOL set
6027 while (--nLine>=0)
6029 if (!IsViewLineEmpty(nLine))
6031 if (GetViewLineEnding(nLine) != EOL_NOENDING)
6033 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6034 // so next line is empty
6035 ASSERT(IsViewLineEmpty(nLine+1));
6036 // and we can turn it to normal empty line
6037 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
6039 break;
6042 // check all (nonlast) line have EOL set
6043 while (--nLine>=0)
6045 if (!IsViewLineEmpty(nLine))
6047 if (GetViewLineEnding(nLine) == EOL_NOENDING)
6049 SetViewLineEnding(nLine, m_lineendings);
6050 // in theory there should be only one line needing fix, but most of time we get over all anyway
6051 // break;
6055 SaveUndoStep();
6056 UpdateViewLineNumbers();
6057 SaveUndoStep();
6059 CUndo::GetInstance().EndGrouping();
6061 if (nRemovedLines!=0)
6063 // some lines are gone update selection
6064 ClearSelection();
6065 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6067 BuildAllScreen2ViewVector();
6068 pwndView->Invalidate();
6069 RefreshViews();
6072 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6074 if (!IsWritable())
6075 return;
6076 CUndo::GetInstance().BeginGrouping();
6078 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6079 SetViewMarked(viewLine, marked);
6081 SetModified();
6082 SaveUndoStep();
6083 CUndo::GetInstance().EndGrouping();
6085 BuildAllScreen2ViewVector();
6086 Invalidate();
6087 RefreshViews();
6090 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6092 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6093 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6096 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6098 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6099 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6102 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6104 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6105 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6108 int CBaseView::GetIndentCharsForLine(int x, int y)
6110 const int maxGuessLine = 100;
6111 int nTabMode = -1;
6112 CString line = GetViewLine(y);
6113 if (m_nTabMode & TABMODE_SMARTINDENT)
6115 // detect left char and right char
6116 TCHAR lc = x > 0 ? line[x - 1] : '\0';
6117 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
6118 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
6119 nTabMode = 1;
6120 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
6121 nTabMode = 0;
6122 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
6123 nTabMode = m_nTabMode & TABMODE_USESPACES;
6125 // detect lines nearby
6126 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6128 bool above = i > 0 && i >= y - maxGuessLine;
6129 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6130 if (!(above || below))
6131 break;
6132 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
6133 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
6134 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
6135 nTabMode = 1;
6136 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
6137 nTabMode = 0;
6138 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
6139 nTabMode = m_nTabMode & TABMODE_USESPACES;
6142 else
6143 nTabMode = m_nTabMode & TABMODE_USESPACES;
6145 if (nTabMode > 0)
6147 // use spaces
6148 x = CountExpandedChars(line, x);
6149 return (m_nTabSize - (x % m_nTabSize));
6152 // use tab
6153 return 0;
6156 void CBaseView::AddIndentationForSelectedBlock()
6158 bool bModified = false;
6159 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6161 // skip the line if no character is selected in the last selected line
6162 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6164 continue;
6166 // skip empty lines
6167 if (IsLineEmpty(nViewLine))
6169 continue;
6171 const CString &sLine = GetViewLine(nViewLine);
6172 CString sTemp = sLine;
6173 if (sTemp.Trim().IsEmpty())
6175 // skip empty and whitechar only lines
6176 continue;
6178 // add tab to line start (alternatively m_nTabSize spaces can be used)
6179 CString tabStr;
6180 int indentChars = GetIndentCharsForLine(0, nViewLine);
6181 tabStr = indentChars > 0 ? CString(L' ', indentChars) : L"\t";
6182 SetViewLine(nViewLine, tabStr + sLine);
6183 bModified = true;
6185 if (bModified)
6187 SetModified();
6188 SaveUndoStep();
6189 BuildAllScreen2ViewVector();
6193 void CBaseView::RemoveIndentationForSelectedBlock()
6195 bool bModified = false;
6196 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6198 // skip the line if no character is selected in the last selected line
6199 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6201 continue;
6203 // skip empty lines
6204 if (IsLineEmpty(nViewLine))
6206 continue;
6208 CString sLine = GetViewLine(nViewLine);
6209 // remove up to n spaces from line start
6210 // and one tab (if less then n spaces was removed)
6211 int nPos = 0;
6212 while (nPos<m_nTabSize)
6214 switch (sLine[nPos])
6216 case ' ':
6217 nPos++;
6218 continue;
6219 case '\t':
6220 nPos++;
6222 break;
6224 if (nPos>0)
6226 sLine.Delete(0, nPos);
6227 SetViewLine(nViewLine, sLine);
6228 bModified = true;
6231 if (bModified)
6233 SetModified();
6234 SaveUndoStep();
6235 BuildAllScreen2ViewVector();
6240 there are two possible versions
6241 - convert tabs to spaces only in front of text (implemented)
6242 - convert all tabs to spaces
6244 void CBaseView::ConvertTabToSpaces()
6246 bool bModified = false;
6247 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6249 if (IsLineEmpty(nViewLine))
6251 continue;
6253 const CString &sLine = GetViewLine(nViewLine);
6254 bool bTabToConvertFound = false;
6255 int nPosIn = 0;
6256 int nPosOut = 0;
6257 while (nPosIn<sLine.GetLength())
6259 switch (sLine[nPosIn])
6261 case ' ':
6262 nPosIn++;
6263 nPosOut++;
6264 continue;
6265 case '\t':
6266 nPosIn++;
6267 bTabToConvertFound = true;
6268 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6269 continue;
6271 break;
6273 if (bTabToConvertFound)
6275 CString sLineNew = sLine;
6276 sLineNew.Delete(0, nPosIn);
6277 sLineNew = CString(' ', nPosOut) + sLineNew;
6278 SetViewLine(nViewLine, sLineNew);
6279 bModified = true;
6282 if (bModified)
6284 SetModified();
6285 SaveUndoStep();
6286 BuildAllScreen2ViewVector();
6291 there are two possible version
6292 - convert spaces to tabs only in front of text (implemented)
6293 - convert all spaces to tabs
6295 void CBaseView::Tabularize()
6297 bool bModified = false;
6298 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6300 if (IsLineEmpty(nViewLine))
6302 continue;
6304 const CString &sLine = GetViewLine(nViewLine);
6305 int nDel = 0;
6306 int nTabCount = 0; // total tabs to be used
6307 int nSpaceCount = 0; // number of spaces in tab size run
6308 int nPos = 0;
6309 while (nPos<sLine.GetLength())
6311 switch (sLine[nPos++])
6313 case ' ':
6314 //bSpace = true;
6315 if (++nSpaceCount < m_nTabSize)
6317 continue;
6319 case '\t':
6320 nTabCount++;
6321 nSpaceCount = 0;
6322 nDel = nPos;
6323 continue;
6325 break;
6327 if (nDel > 0)
6329 CString sLineNew = sLine;
6330 sLineNew.Delete(0, nDel);
6331 sLineNew = CString('\t', nTabCount) + sLineNew;
6332 if (sLine!=sLineNew)
6334 SetViewLine(nViewLine, sLineNew);
6335 bModified = true;
6339 if (bModified)
6341 SetModified();
6342 SaveUndoStep();
6343 BuildAllScreen2ViewVector();
6347 void CBaseView::RemoveTrailWhiteChars()
6349 bool bModified = false;
6350 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6352 if (IsLineEmpty(nViewLine))
6354 continue;
6356 const CString &sLine = GetViewLine(nViewLine);
6357 CString sLineNew = sLine;
6358 sLineNew.TrimRight();
6359 if (sLine.GetLength()!=sLineNew.GetLength())
6361 SetViewLine(nViewLine, sLineNew);
6362 bModified = true;
6365 if (bModified)
6367 SetModified();
6368 SaveUndoStep();
6369 BuildAllScreen2ViewVector();
6373 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6375 if (GetViewCount()>10000)
6377 // 10k lines is enough to check
6378 TWhitecharsProperties oRet = {true, true, true, true};
6379 return oRet;
6381 TWhitecharsProperties oRet = {};
6382 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6384 if (IsLineEmpty(nViewLine))
6386 continue;
6388 const CString &sLine = GetViewLine(nViewLine);
6389 if (sLine.IsEmpty())
6391 continue;
6393 // check leading whites for convertible tabs and spaces
6394 int nPos = 0;
6395 int nSpaceCount = 0; // number of spaces in tab size run
6396 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6398 switch (sLine[nPos++])
6400 case ' ':
6401 if (++nSpaceCount >= m_nTabSize)
6403 oRet.HasSpacesToConvert = true;
6405 continue;
6406 case '\t':
6407 oRet.HasTabsToConvert = true;
6408 if (nSpaceCount!=0)
6410 oRet.HasSpacesToConvert = true;
6412 continue;
6414 break;
6417 // check trailing whites for removable chars
6418 switch (sLine[sLine.GetLength()-1])
6420 case ' ':
6421 case '\t':
6422 oRet.HasTrailWhiteChars = true;
6425 // check EOLs
6426 EOL eLineEol = GetViewLineEnding(nViewLine);
6427 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6429 oRet.HasMixedEols = true;
6432 return oRet;
6435 void CBaseView::InsertText(const CString& sText)
6437 ResetUndoStep();
6439 POINT ptCaretViewPos = GetCaretViewPosition();
6440 int nLeft = ptCaretViewPos.x;
6441 int nViewLine = ptCaretViewPos.y;
6443 if ((nViewLine == 0) && (GetViewCount() == 0))
6444 OnChar(VK_RETURN, 0, 0);
6446 std::vector<CString> lines;
6447 int nStart = 0;
6448 int nEolPos = 0;
6449 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6451 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6452 lines.push_back(sLine);
6453 nEolPos++;
6454 nStart = nEolPos;
6456 CString sLine = sText.Mid(nStart);
6457 lines.push_back(sLine);
6459 int nLinesToPaste = (int)lines.size();
6460 if (nLinesToPaste > 1)
6462 // multiline text
6464 // We want to undo the multiline insertion in a single step.
6465 CUndo::GetInstance().BeginGrouping();
6467 sLine = GetViewLineChars(nViewLine);
6468 CString sLineLeft = sLine.Left(nLeft);
6469 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6470 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6471 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6472 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6474 newLine.sLine = sLineLeft + lines[0];
6475 SetViewData(nViewLine, newLine);
6478 int nInsertLine = nViewLine;
6479 for (int i = 1; i < nLinesToPaste - 1; i++)
6481 newLine.sLine = lines[i];
6482 InsertViewData(++nInsertLine, newLine);
6484 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6485 newLine.ending = eOriginalEnding;
6486 InsertViewData(++nInsertLine, newLine);
6488 SetModified();
6489 SaveUndoStep();
6491 // adds new lines everywhere except me
6492 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6494 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6496 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6498 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6500 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6502 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6504 SaveUndoStep();
6506 UpdateViewLineNumbers();
6507 CUndo::GetInstance().EndGrouping();
6509 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6511 else
6513 // single line text - just insert it
6514 sLine = GetViewLineChars(nViewLine);
6515 sLine.Insert(nLeft, sText);
6516 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6517 SetViewLine(nViewLine, sLine);
6518 SetViewState(nViewLine, DIFFSTATE_EDITED);
6519 SetModified();
6520 SaveUndoStep();
6523 RefreshViews();
6524 BuildAllScreen2ViewVector();
6525 UpdateCaretViewPosition(ptCaretViewPos);