Sync translations with Transifex
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob0434742373a3644a7f63845fd6d254022477d8aa
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2014 - TortoiseSVN
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
28 #include "AppUtils.h"
29 #include "GotoLineDlg.h"
30 #include "EncodingDlg.h"
31 #include "EditorConfigWrapper.h"
33 // Note about lines:
34 // We use three different kind of lines here:
35 // 1. The real lines of the original files.
36 // These are shown in the view margin and are not used elsewhere, they're only for user information.
37 // 2. Screen lines.
38 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
39 // 3. View lines.
40 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
42 // Basically view lines are the line data, while screen lines are shown lines.
45 #ifdef _DEBUG
46 #define new DEBUG_NEW
47 #endif
49 #define MARGINWIDTH 20
50 #define HEADERHEIGHT 10
52 #define IDT_SCROLLTIMER 101
54 CBaseView * CBaseView::m_pwndLeft = NULL;
55 CBaseView * CBaseView::m_pwndRight = NULL;
56 CBaseView * CBaseView::m_pwndBottom = NULL;
57 CLocatorBar * CBaseView::m_pwndLocator = NULL;
58 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
59 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
60 CMFCRibbonStatusBar * CBaseView::m_pwndRibbonStatusBar = NULL;
61 CMainFrame * CBaseView::m_pMainFrame = NULL;
62 CBaseView::Screen2View CBaseView::m_Screen2View;
63 const UINT CBaseView::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
65 allviewstate CBaseView::m_AllState;
67 IMPLEMENT_DYNCREATE(CBaseView, CView)
69 CBaseView::CBaseView()
70 : m_pCacheBitmap(NULL)
71 , m_pViewData(NULL)
72 , m_pOtherViewData(NULL)
73 , m_pOtherView(NULL)
74 , m_nLineHeight(-1)
75 , m_nCharWidth(-1)
76 , m_nScreenChars(-1)
77 , m_nLastScreenChars(-1)
78 , m_nMaxLineLength(-1)
79 , m_nScreenLines(-1)
80 , m_nTopLine(0)
81 , m_nOffsetChar(0)
82 , m_nDigits(0)
83 , m_nMouseLine(-1)
84 , m_mouseInMargin(false)
85 , m_bIsHidden(FALSE)
86 , m_lineendings(EOL_AUTOLINE)
87 , m_bReadonly(true)
88 , m_bReadonlyIsChangable(false)
89 , m_bTarget(false)
90 , m_nCaretGoalPos(0)
91 , m_nSelViewBlockStart(-1)
92 , m_nSelViewBlockEnd(-1)
93 , m_bFocused(FALSE)
94 , m_bShowSelection(true)
95 , m_texttype(CFileTextLines::AUTOTYPE)
96 , m_bModified(FALSE)
97 , m_bOtherDiffChecked(false)
98 , m_bInlineWordDiff(true)
99 , m_bWhitespaceInlineDiffs(false)
100 , m_pState(NULL)
101 , m_pFindDialog(NULL)
102 , m_nStatusBarID(0)
103 , m_bMatchCase(false)
104 , m_bLimitToDiff(true)
105 , m_bWholeWord(false)
106 , m_pDC(NULL)
107 , m_pWorkingFile(NULL)
108 , m_bInsertMode(true)
109 , m_bEditorConfigEnabled(false)
110 , m_bEditorConfigLoaded(2) // 2 = not evaluated
112 m_ptCaretViewPos.x = 0;
113 m_ptCaretViewPos.y = 0;
114 m_ptSelectionViewPosStart = m_ptCaretViewPos;
115 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
116 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
117 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
118 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
119 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
120 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
121 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
122 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
123 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
124 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
125 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?"));
126 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
127 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
128 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
129 m_bEditorConfigEnabled = !!(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\EnableEditorConfig"), FALSE);
130 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
131 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
132 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
133 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
134 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
135 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
136 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
137 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
138 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
139 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
140 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
141 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
142 m_hMarkedIcon = LoadIcon(IDI_LINEMARKED);
143 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
145 for (int i=0; i<1024; ++i)
146 m_sConflictedText += _T("??");
147 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
149 m_szTip[0] = 0;
150 m_wszTip[0] = 0;
151 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
152 EnableToolTips();
154 m_Eols[EOL_LF] = L"\n"; // x0a
155 m_Eols[EOL_CR] = L"\r"; // x0d
156 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
157 m_Eols[EOL_LFCR] = L"\n\r";
158 m_Eols[EOL_VT] = L"\v"; // x0b
159 m_Eols[EOL_FF] = L"\f"; // x0c
160 m_Eols[EOL_NEL] = L"\x85";
161 m_Eols[EOL_LS] = L"\x2028";
162 m_Eols[EOL_PS] = L"\x2029";
163 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
164 ? EOL_CRLF
165 : m_lineendings];
166 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
167 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
170 CBaseView::~CBaseView()
172 ReleaseBitmap();
173 DeleteFonts();
174 DestroyIcon(m_hAddedIcon);
175 DestroyIcon(m_hRemovedIcon);
176 DestroyIcon(m_hConflictedIcon);
177 DestroyIcon(m_hConflictedIgnoredIcon);
178 DestroyIcon(m_hWhitespaceBlockIcon);
179 DestroyIcon(m_hEqualIcon);
180 DestroyIcon(m_hLineEndingCR);
181 DestroyIcon(m_hLineEndingCRLF);
182 DestroyIcon(m_hLineEndingLF);
183 DestroyIcon(m_hEditedIcon);
184 DestroyIcon(m_hMovedIcon);
185 DestroyIcon(m_hMarkedIcon);
186 DestroyCursor(m_margincursor);
189 BEGIN_MESSAGE_MAP(CBaseView, CView)
190 ON_WM_VSCROLL()
191 ON_WM_HSCROLL()
192 ON_WM_ERASEBKGND()
193 ON_WM_CREATE()
194 ON_WM_DESTROY()
195 ON_WM_SIZE()
196 ON_WM_MOUSEWHEEL()
197 ON_WM_MOUSEHWHEEL()
198 ON_WM_SETCURSOR()
199 ON_WM_KILLFOCUS()
200 ON_WM_SETFOCUS()
201 ON_WM_CONTEXTMENU()
202 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
203 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
204 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
205 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
206 ON_WM_KEYDOWN()
207 ON_WM_LBUTTONDOWN()
208 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
209 ON_WM_MOUSEMOVE()
210 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
211 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
212 ON_WM_CHAR()
213 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
214 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
215 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
216 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
217 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
218 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
219 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
220 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
221 ON_WM_TIMER()
222 ON_WM_LBUTTONDBLCLK()
223 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
224 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
225 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
226 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
227 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
228 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
229 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
230 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
231 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
232 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
233 ON_WM_LBUTTONUP()
234 END_MESSAGE_MAP()
237 void CBaseView::DocumentUpdated()
239 ReleaseBitmap();
240 m_nLineHeight = -1;
241 m_nCharWidth = -1;
242 m_nScreenChars = -1;
243 m_nLastScreenChars = -1;
244 m_nMaxLineLength = -1;
245 m_nScreenLines = -1;
246 m_nTopLine = 0;
247 m_bModified = FALSE;
248 m_bOtherDiffChecked = false;
249 m_nDigits = 0;
250 m_nMouseLine = -1;
251 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
252 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
253 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
254 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
255 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
256 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
257 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
258 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
259 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
260 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
261 ? EOL_CRLF
262 : m_lineendings];
263 SetEditorConfigEnabled(m_bEditorConfigEnabled);
264 DeleteFonts();
265 ClearCurrentSelection();
266 UpdateStatusBar();
267 Invalidate();
270 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled)
272 m_bEditorConfigEnabled = bEditorConfigEnabled;
273 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
274 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
275 if (m_bEditorConfigEnabled)
277 m_bEditorConfigLoaded = FALSE; // no editorconfig entries loaded
278 CEditorConfigWrapper ec;
279 if (ec.Load(m_sReflectedName.IsEmpty() ? m_sFullFilePath : m_sReflectedName))
281 m_bEditorConfigLoaded = TRUE;
282 if (ec.m_nTabWidth != nullptr)
283 m_nTabSize = ec.m_nTabWidth;
284 if (ec.m_bIndentStyle != nullptr)
285 m_nTabMode = (m_nTabMode & ~TABMODE_USESPACES) | (ec.m_bIndentStyle ? TABMODE_USESPACES : TABMODE_NONE);
290 static CString GetTabModeString(int nTabMode, int nTabSize, bool bEditorConfig)
292 CString text;
293 if (nTabMode & TABMODE_USESPACES)
294 text = L"Space";
295 else
296 text = L"Tab";
297 text.AppendFormat(L" %d", nTabSize);
298 if (nTabMode & TABMODE_SMARTINDENT)
299 text += L" Smart";
300 if (bEditorConfig)
301 text += L" EC";
302 return text;
305 void CBaseView::UpdateStatusBar()
307 int nRemovedLines = 0;
308 int nAddedLines = 0;
309 int nConflictedLines = 0;
311 if (m_pViewData)
313 for (int i=0; i<m_pViewData->GetCount(); i++)
315 DiffStates state = m_pViewData->GetState(i);
316 switch (state)
318 case DIFFSTATE_ADDED:
319 case DIFFSTATE_IDENTICALADDED:
320 case DIFFSTATE_THEIRSADDED:
321 case DIFFSTATE_YOURSADDED:
322 case DIFFSTATE_CONFLICTADDED:
323 nAddedLines++;
324 break;
325 case DIFFSTATE_IDENTICALREMOVED:
326 case DIFFSTATE_REMOVED:
327 case DIFFSTATE_THEIRSREMOVED:
328 case DIFFSTATE_YOURSREMOVED:
329 nRemovedLines++;
330 break;
331 case DIFFSTATE_CONFLICTED:
332 case DIFFSTATE_CONFLICTED_IGNORED:
333 nConflictedLines++;
334 break;
339 CString sBarText;
340 CString sTemp;
342 if (m_pwndStatusBar)
344 sBarText += CFileTextLines::GetEncodingName(m_texttype);
345 sBarText += sBarText.IsEmpty() ? L"" : L" ";
346 sBarText += GetEolName(m_lineendings);
347 sBarText += sBarText.IsEmpty() ? L"" : L" ";
349 if (sBarText.IsEmpty())
350 sBarText += _T(" / ");
353 if (nRemovedLines)
355 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
356 if (!sBarText.IsEmpty())
357 sBarText += _T(" / ");
358 sBarText += sTemp;
360 if (nAddedLines)
362 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
363 if (!sBarText.IsEmpty())
364 sBarText += _T(" / ");
365 sBarText += sTemp;
367 if (nConflictedLines)
369 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
370 if (!sBarText.IsEmpty())
371 sBarText += _T(" / ");
372 sBarText += sTemp;
374 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
376 if (m_pwndStatusBar)
378 UINT nID;
379 UINT nStyle;
380 int cxWidth;
381 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
383 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
385 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
387 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
388 sBarText = sTemp+sBarText;
390 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
392 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
393 sBarText = sTemp+sBarText;
395 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
396 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
397 //calculate the width of the text
398 CDC * pDC = m_pwndStatusBar->GetDC();
399 if (pDC)
401 CSize size = pDC->GetTextExtent(sBarText);
402 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
403 ReleaseDC(pDC);
405 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
407 else if (m_pwndRibbonStatusBar)
409 if (!IsViewGood(m_pwndBottom))
410 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
411 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
413 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
414 std::unique_ptr<CMFCRibbonButtonsGroup> apBtnGroupBottom(new CMFCRibbonButtonsGroup);
415 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
416 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
417 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
418 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
419 apBtnGroupBottom->AddButton(pButton);
420 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
421 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
422 apBtnGroupBottom->AddButton(pButton);
423 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, L"");
424 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMTABMODESTART);
425 apBtnGroupBottom->AddButton(pButton);
426 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
427 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
430 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
431 if (pGroup)
433 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(4));
434 if (pPane)
436 pPane->SetText(sBarText);
438 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
439 if (pButton)
441 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
442 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
444 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
445 if (pButton)
447 pButton->SetText(GetEolName(m_lineendings));
448 pButton->SetDescription(GetEolName(m_lineendings));
450 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(3));
451 if (pButton)
453 pButton->SetText(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
454 pButton->SetDescription(GetTabModeString(m_nTabMode, m_nTabSize, m_bEditorConfigEnabled && m_bEditorConfigLoaded));
457 m_pwndRibbonStatusBar->RecalcLayout();
458 m_pwndRibbonStatusBar->Invalidate();
463 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
465 if (!CView::PreCreateWindow(cs))
466 return FALSE;
468 cs.dwExStyle |= WS_EX_CLIENTEDGE;
469 cs.style &= ~WS_BORDER;
470 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
471 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
473 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
474 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
476 // View must always create its own scrollbars,
477 // if only it's not used within splitter
478 cs.style |= (WS_HSCROLL | WS_VSCROLL);
480 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
481 return TRUE;
484 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
486 int nIndex = 0;
487 if (bBold)
488 nIndex |= 1;
489 if (bItalic)
490 nIndex |= 2;
491 if (m_apFonts[nIndex] == NULL)
493 m_apFonts[nIndex] = new CFont;
494 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
495 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
496 m_lfBaseFont.lfItalic = (BYTE) bItalic;
497 CDC * pDC = GetDC();
498 if (pDC)
500 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
501 ReleaseDC(pDC);
503 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
504 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
506 delete m_apFonts[nIndex];
507 m_apFonts[nIndex] = NULL;
508 return CView::GetFont();
511 return m_apFonts[nIndex];
514 void CBaseView::CalcLineCharDim()
516 CDC *pDC = GetDC();
517 if (pDC == nullptr)
518 return;
519 CFont *pOldFont = pDC->SelectObject(GetFont());
520 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
521 pDC->SelectObject(pOldFont);
522 ReleaseDC(pDC);
524 m_nLineHeight = szCharExt.cy;
525 if (m_nLineHeight <= 0)
526 m_nLineHeight = -1;
527 m_nCharWidth = szCharExt.cx;
528 if (m_nCharWidth <= 0)
529 m_nCharWidth = -1;
532 int CBaseView::GetScreenChars()
534 if (m_nScreenChars == -1)
536 CRect rect;
537 GetClientRect(&rect);
538 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
539 if (m_nScreenChars < 0)
540 m_nScreenChars = 0;
542 return m_nScreenChars;
545 int CBaseView::GetAllMinScreenChars() const
547 int nChars = INT_MAX;
548 if (IsLeftViewGood())
549 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
550 if (IsRightViewGood())
551 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
552 if (IsBottomViewGood())
553 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
554 return (nChars==INT_MAX) ? 0 : nChars;
557 int CBaseView::GetAllMaxLineLength() const
559 int nLength = 0;
560 if (IsLeftViewGood())
561 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
562 if (IsRightViewGood())
563 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
564 if (IsBottomViewGood())
565 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
566 return nLength;
569 int CBaseView::GetLineHeight()
571 if (m_nLineHeight == -1)
572 CalcLineCharDim();
573 if (m_nLineHeight <= 0)
574 return 1;
575 return m_nLineHeight;
578 int CBaseView::GetCharWidth()
580 if (m_nCharWidth == -1)
581 CalcLineCharDim();
582 if (m_nCharWidth <= 0)
583 return 1;
584 return m_nCharWidth;
587 int CBaseView::GetMaxLineLength()
589 if (m_nMaxLineLength == -1)
591 m_nMaxLineLength = 0;
592 int nLineCount = GetLineCount();
593 for (int i=0; i<nLineCount; i++)
595 int nActualLength = GetLineLength(i);
596 if (m_nMaxLineLength < nActualLength)
597 m_nMaxLineLength = nActualLength;
600 return m_nMaxLineLength;
603 int CBaseView::GetLineLength(int index)
605 if (m_pViewData == NULL)
606 return 0;
607 if (m_pViewData->GetCount() == 0)
608 return 0;
609 if ((int)m_Screen2View.size() <= index)
610 return 0;
611 int viewLine = GetViewLineForScreen(index);
612 if (m_pMainFrame->m_bWrapLines)
614 int nLineLength = GetLineChars(index).GetLength();
615 ASSERT(nLineLength >= 0);
616 return nLineLength;
618 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
619 ASSERT(nLineLength >= 0);
620 return nLineLength;
623 int CBaseView::GetViewLineLength(int nViewLine) const
625 if (m_pViewData == NULL)
626 return 0;
627 if (m_pViewData->GetCount() <= nViewLine)
628 return 0;
629 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
630 ASSERT(nLineLength >= 0);
631 return nLineLength;
634 int CBaseView::GetLineCount() const
636 if (m_pViewData == NULL)
637 return 1;
638 int nLineCount = (int)m_Screen2View.size();
639 ASSERT(nLineCount >= 0);
640 return nLineCount;
643 int CBaseView::GetSubLineOffset(int index)
645 return m_Screen2View.GetSubLineOffset(index);
648 CString CBaseView::GetViewLineChars(int nViewLine) const
650 if (m_pViewData == NULL)
651 return 0;
652 if (m_pViewData->GetCount() <= nViewLine)
653 return 0;
654 return m_pViewData->GetLine(nViewLine);
657 CString CBaseView::GetLineChars(int index)
659 if (m_pViewData == NULL)
660 return 0;
661 if (m_pViewData->GetCount() == 0)
662 return 0;
663 if ((int)m_Screen2View.size() <= index)
664 return 0;
665 int viewLine = GetViewLineForScreen(index);
666 if (m_pMainFrame->m_bWrapLines)
668 int subLine = GetSubLineOffset(index);
669 if (subLine >= 0)
671 if (subLine < CountMultiLines(viewLine))
673 return m_ScreenedViewLine[viewLine].SubLines[subLine];
675 return L"";
678 return m_pViewData->GetLine(viewLine);
681 void CBaseView::CheckOtherView()
683 if (m_bOtherDiffChecked)
684 return;
685 // find out what the 'other' file is
686 m_pOtherViewData = NULL;
687 m_pOtherView = NULL;
688 if (this == m_pwndLeft && IsRightViewGood())
690 m_pOtherViewData = m_pwndRight->m_pViewData;
691 m_pOtherView = m_pwndRight;
694 if (this == m_pwndRight && IsLeftViewGood())
696 m_pOtherViewData = m_pwndLeft->m_pViewData;
697 m_pOtherView = m_pwndLeft;
700 m_bOtherDiffChecked = true;
704 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
706 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
707 ASSERT(viewData);
709 DiffStates origstate = viewData->GetState(nLineIndex);
711 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
712 nStartBlock = nLineIndex;
713 nEndBlock = nLineIndex;
714 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
716 DiffStates state = viewData->GetState(nStartBlock - 1);
717 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
718 origstate = state;
719 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
720 nStartBlock--;
721 else
722 break;
724 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
726 DiffStates state = viewData->GetState(nEndBlock + 1);
727 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
728 origstate = state;
729 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
730 nEndBlock++;
731 else
732 break;
736 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
738 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
740 int len = 0;
741 for (int i = nStartBlock; i <= nEndBlock; ++i)
742 len += viewData->GetLine(i).GetLength()+2;
744 CString block;
745 // do not check for whitespace blocks if the line is too long, because
746 // reserving a lot of memory here takes too much time (performance hog)
747 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
748 return block;
749 block.Preallocate(len+1);
750 for (int i = nStartBlock; i <= nEndBlock; ++i)
752 block += viewData->GetLine(i);
753 block += m_Eols[viewData->GetLineEnding(i)];
755 return block;
758 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
760 if (m_pViewData == NULL)
761 return false;
762 bIdentical = false;
763 CheckOtherView();
764 if (!m_pOtherViewData)
765 return false;
766 int viewLine = GetViewLineForScreen(nLineIndex);
767 if (
768 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
769 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
772 bIdentical = true;
773 return false;
775 // first check whether the line itself only has whitespace changes
776 CString mine = m_pViewData->GetLine(viewLine);
777 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
778 if (mine.IsEmpty() && other.IsEmpty())
780 bIdentical = true;
781 return false;
784 if (mine == other)
786 bIdentical = true;
787 return true;
789 FilterWhitespaces(mine, other);
790 if (mine == other)
791 return true;
793 int nStartBlock2, nEndBlock2;
794 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
795 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
796 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
797 if (mine.IsEmpty())
798 bIdentical = false;
799 else
801 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
802 bIdentical = mine == other;
803 FilterWhitespaces(mine, other);
806 return (!mine.IsEmpty()) && (mine == other);
809 bool CBaseView::IsViewLineHidden(int nViewLine)
811 return IsViewLineHidden(m_pViewData, nViewLine);
814 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
816 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
819 int CBaseView::GetLineNumber(int index) const
821 if (m_pViewData == NULL)
822 return -1;
823 int viewLine = GetViewLineForScreen(index);
824 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
825 return -1;
826 return m_pViewData->GetLineNumber(viewLine);
829 int CBaseView::GetScreenLines()
831 if (m_nScreenLines == -1)
833 SCROLLBARINFO sbi;
834 sbi.cbSize = sizeof(sbi);
835 int scrollBarHeight = 0;
836 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
837 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
838 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
839 scrollBarHeight = 0;
840 CRect rect;
841 GetClientRect(&rect);
842 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
843 if (m_nScreenLines < 0)
844 m_nScreenLines = 0;
846 return m_nScreenLines;
849 int CBaseView::GetAllMinScreenLines() const
851 int nLines = INT_MAX;
852 if (IsLeftViewGood())
853 nLines = m_pwndLeft->GetScreenLines();
854 if (IsRightViewGood())
855 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
856 if (IsBottomViewGood())
857 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
858 return (nLines==INT_MAX) ? 0 : nLines;
861 int CBaseView::GetAllLineCount() const
863 int nLines = 0;
864 if (IsLeftViewGood())
865 nLines = m_pwndLeft->GetLineCount();
866 if (IsRightViewGood())
867 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
868 if (IsBottomViewGood())
869 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
870 return nLines;
873 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
875 if (IsLeftViewGood())
876 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
877 if (IsRightViewGood())
878 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
879 if (IsBottomViewGood())
880 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
883 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
885 SCROLLINFO si;
886 si.cbSize = sizeof(si);
887 if (bPositionOnly)
889 si.fMask = SIF_POS;
890 si.nPos = m_nTopLine;
892 else
894 EnableScrollBarCtrl(SB_VERT, TRUE);
895 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
897 m_nTopLine = 0;
898 Invalidate();
900 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
901 si.nMin = 0;
902 si.nMax = GetAllLineCount();
903 si.nPage = GetAllMinScreenLines();
904 si.nPos = m_nTopLine;
906 VERIFY(SetScrollInfo(SB_VERT, &si));
909 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
911 CView::OnVScroll(nSBCode, nPos, pScrollBar);
912 if (m_pwndLeft)
913 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
914 if (m_pwndRight)
915 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
916 if (m_pwndBottom)
917 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
918 if (m_pwndLocator)
919 m_pwndLocator->Invalidate();
922 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
924 // Note we cannot use nPos because of its 16-bit nature
925 SCROLLINFO si;
926 si.cbSize = sizeof(si);
927 si.fMask = SIF_ALL;
928 VERIFY(master->GetScrollInfo(SB_VERT, &si));
930 int nPageLines = GetScreenLines();
931 int nLineCount = GetLineCount();
933 int nNewTopLine;
935 static LONG textwidth = 0;
936 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
937 switch (nSBCode)
939 case SB_TOP:
940 nNewTopLine = 0;
941 break;
942 case SB_BOTTOM:
943 nNewTopLine = nLineCount - nPageLines + 1;
944 break;
945 case SB_LINEUP:
946 nNewTopLine = m_nTopLine - 1;
947 break;
948 case SB_LINEDOWN:
949 nNewTopLine = m_nTopLine + 1;
950 break;
951 case SB_PAGEUP:
952 nNewTopLine = m_nTopLine - si.nPage + 1;
953 break;
954 case SB_PAGEDOWN:
955 nNewTopLine = m_nTopLine + si.nPage - 1;
956 break;
957 case SB_THUMBPOSITION:
958 m_ScrollTool.Clear();
959 nNewTopLine = si.nTrackPos;
960 textwidth = 0;
961 break;
962 case SB_THUMBTRACK:
963 nNewTopLine = si.nTrackPos;
964 if (GetFocus() == this)
966 RECT thumbrect;
967 GetClientRect(&thumbrect);
968 ClientToScreen(&thumbrect);
970 POINT thumbpoint;
971 thumbpoint.x = thumbrect.right;
972 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
973 m_ScrollTool.Init(&thumbpoint);
974 if (textwidth == 0)
976 CString sTemp = sFormat;
977 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
978 textwidth = m_ScrollTool.GetTextWidth(sTemp);
980 thumbpoint.x -= textwidth;
981 int line = GetLineNumber(nNewTopLine);
982 if (line >= 0)
983 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
984 else
985 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
987 break;
988 default:
989 return;
992 if (nNewTopLine < 0)
993 nNewTopLine = 0;
994 if (nNewTopLine >= nLineCount)
995 nNewTopLine = nLineCount - 1;
996 ScrollToLine(nNewTopLine);
999 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
1001 if (IsLeftViewGood())
1002 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
1003 if (IsRightViewGood())
1004 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
1005 if (IsBottomViewGood())
1006 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
1009 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
1011 SCROLLINFO si;
1012 si.cbSize = sizeof(si);
1013 if (bPositionOnly)
1015 si.fMask = SIF_POS;
1016 si.nPos = m_nOffsetChar;
1018 else
1020 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
1021 if (!m_pMainFrame->m_bWrapLines)
1023 int minScreenChars = GetAllMinScreenChars();
1024 int maxLineLength = GetAllMaxLineLength();
1025 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1027 m_nOffsetChar = 0;
1028 Invalidate();
1030 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1031 si.nMin = 0;
1032 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1033 si.nMax += GetMarginWidth()/GetCharWidth();
1034 si.nPage = GetScreenChars();
1035 si.nPos = m_nOffsetChar;
1038 VERIFY(SetScrollInfo(SB_HORZ, &si));
1041 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1043 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1044 if (m_pwndLeft)
1045 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1046 if (m_pwndRight)
1047 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1048 if (m_pwndBottom)
1049 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1050 if (m_pwndLocator)
1051 m_pwndLocator->Invalidate();
1054 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1056 SCROLLINFO si;
1057 si.cbSize = sizeof(si);
1058 si.fMask = SIF_ALL;
1059 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1061 int nPageChars = GetScreenChars();
1062 int nMaxLineLength = GetMaxLineLength();
1064 int nNewOffset;
1065 switch (nSBCode)
1067 case SB_LEFT:
1068 nNewOffset = 0;
1069 break;
1070 case SB_BOTTOM:
1071 nNewOffset = nMaxLineLength - nPageChars + 1;
1072 break;
1073 case SB_LINEUP:
1074 nNewOffset = m_nOffsetChar - 1;
1075 break;
1076 case SB_LINEDOWN:
1077 nNewOffset = m_nOffsetChar + 1;
1078 break;
1079 case SB_PAGEUP:
1080 nNewOffset = m_nOffsetChar - si.nPage + 1;
1081 break;
1082 case SB_PAGEDOWN:
1083 nNewOffset = m_nOffsetChar + si.nPage - 1;
1084 break;
1085 case SB_THUMBPOSITION:
1086 case SB_THUMBTRACK:
1087 nNewOffset = si.nTrackPos;
1088 break;
1089 default:
1090 return;
1093 if (nNewOffset >= nMaxLineLength)
1094 nNewOffset = nMaxLineLength - 1;
1095 if (nNewOffset < 0)
1096 nNewOffset = 0;
1097 ScrollToChar(nNewOffset, TRUE);
1100 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1102 if (m_nOffsetChar != nNewOffsetChar)
1104 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1105 m_nOffsetChar = nNewOffsetChar;
1106 CRect rcScroll;
1107 GetClientRect(&rcScroll);
1108 rcScroll.left += GetMarginWidth();
1109 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1110 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1111 // update the view header
1112 rcScroll.left = 0;
1113 rcScroll.top = 0;
1114 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1115 InvalidateRect(&rcScroll, FALSE);
1116 UpdateWindow();
1117 if (bTrackScrollBar)
1118 RecalcHorzScrollBar(TRUE);
1119 UpdateCaret();
1120 if (m_pwndLineDiffBar)
1121 m_pwndLineDiffBar->Invalidate();
1125 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1127 if (m_pwndLeft)
1128 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1129 if (m_pwndRight)
1130 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1131 if (m_pwndBottom)
1132 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1135 void CBaseView::ScrollAllSide(int delta)
1137 int nNewOffset = m_nOffsetChar;
1138 nNewOffset += delta;
1139 int nMaxLineLength = GetMaxLineLength();
1140 if (nNewOffset >= nMaxLineLength)
1141 nNewOffset = nMaxLineLength - 1;
1142 if (nNewOffset < 0)
1143 nNewOffset = 0;
1144 ScrollAllToChar(nNewOffset, TRUE);
1145 if (m_pwndLineDiffBar)
1146 m_pwndLineDiffBar->Invalidate();
1147 UpdateCaret();
1150 void CBaseView::ScrollSide(int delta)
1152 int nNewOffset = m_nOffsetChar;
1153 nNewOffset += delta;
1154 int nMaxLineLength = GetMaxLineLength();
1155 if (nNewOffset >= nMaxLineLength)
1156 nNewOffset = nMaxLineLength - 1;
1157 if (nNewOffset < 0)
1158 nNewOffset = 0;
1159 ScrollToChar(nNewOffset, TRUE);
1160 if (m_pwndLineDiffBar)
1161 m_pwndLineDiffBar->Invalidate();
1162 UpdateCaret();
1165 void CBaseView::ScrollVertical(short zDelta)
1167 const int nLineCount = GetLineCount();
1168 int nTopLine = m_nTopLine;
1169 nTopLine -= (zDelta/30);
1170 if (nTopLine < 0)
1171 nTopLine = 0;
1172 if (nTopLine >= nLineCount)
1173 nTopLine = nLineCount - 1;
1174 ScrollToLine(nTopLine, TRUE);
1177 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1179 if (m_nTopLine != nNewTopLine)
1181 if (nNewTopLine < 0)
1182 nNewTopLine = 0;
1184 int nScrollLines = m_nTopLine - nNewTopLine;
1186 m_nTopLine = nNewTopLine;
1187 CRect rcScroll;
1188 GetClientRect(&rcScroll);
1189 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1190 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1191 UpdateWindow();
1192 if (bTrackScrollBar)
1193 RecalcVertScrollBar(TRUE);
1194 UpdateCaret();
1199 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1201 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1203 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1205 int nViewLine = GetViewLineForScreen(nLineIndex);
1206 HICON icon = NULL;
1207 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1208 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1209 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1211 DiffStates state = m_pViewData->GetState(nViewLine);
1212 switch (state)
1214 case DIFFSTATE_ADDED:
1215 case DIFFSTATE_THEIRSADDED:
1216 case DIFFSTATE_YOURSADDED:
1217 case DIFFSTATE_IDENTICALADDED:
1218 case DIFFSTATE_CONFLICTADDED:
1219 eIcon = TScreenedViewLine::ICN_ADD;
1220 break;
1221 case DIFFSTATE_REMOVED:
1222 case DIFFSTATE_THEIRSREMOVED:
1223 case DIFFSTATE_YOURSREMOVED:
1224 case DIFFSTATE_IDENTICALREMOVED:
1225 eIcon = TScreenedViewLine::ICN_REMOVED;
1226 break;
1227 case DIFFSTATE_CONFLICTED:
1228 eIcon = TScreenedViewLine::ICN_CONFLICT;
1229 break;
1230 case DIFFSTATE_CONFLICTED_IGNORED:
1231 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1232 break;
1233 case DIFFSTATE_EDITED:
1234 eIcon = TScreenedViewLine::ICN_EDIT;
1235 break;
1236 default:
1237 break;
1239 bool bIdentical = false;
1240 int blockstart = -1;
1241 int blockend = -1;
1242 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1244 if (bIdentical)
1245 eIcon = TScreenedViewLine::ICN_SAME;
1246 else
1247 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1248 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1250 if (nViewLine > blockstart)
1251 Invalidate(); // redraw the upper icons since they're now changing
1252 while (blockstart <= blockend)
1253 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1256 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1257 eIcon = TScreenedViewLine::ICN_MOVED;
1258 if (m_pViewData->GetMarked(nViewLine))
1259 eIcon = TScreenedViewLine::ICN_MARKED;
1260 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1262 switch (eIcon)
1264 case TScreenedViewLine::ICN_UNKNOWN:
1265 case TScreenedViewLine::ICN_NONE:
1266 break;
1267 case TScreenedViewLine::ICN_SAME:
1268 icon = m_hEqualIcon;
1269 break;
1270 case TScreenedViewLine::ICN_EDIT:
1271 icon = m_hEditedIcon;
1272 break;
1273 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1274 icon = m_hWhitespaceBlockIcon;
1275 break;
1276 case TScreenedViewLine::ICN_ADD:
1277 icon = m_hAddedIcon;
1278 break;
1279 case TScreenedViewLine::ICN_CONFLICT:
1280 icon = m_hConflictedIcon;
1281 break;
1282 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1283 icon = m_hConflictedIgnoredIcon;
1284 break;
1285 case TScreenedViewLine::ICN_REMOVED:
1286 icon = m_hRemovedIcon;
1287 break;
1288 case TScreenedViewLine::ICN_MOVED:
1289 icon = m_hMovedIcon;
1290 break;
1291 case TScreenedViewLine::ICN_MARKED:
1292 icon = m_hMarkedIcon;
1293 break;
1297 if (icon)
1299 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1301 if ((m_bViewLinenumbers)&&(m_nDigits))
1303 int nSubLine = GetSubLineOffset(nLineIndex);
1304 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1305 CString sLinenumber;
1306 if (bIsFirstSubline)
1308 CString sLinenumberFormat;
1309 int nLineNumber = GetLineNumber(nLineIndex);
1310 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1312 // TODO: do not show if there is no number hidden
1313 // TODO: show number if there is only one
1314 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1315 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1317 else if (nLineNumber >= 0)
1319 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1320 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1322 else if (m_pMainFrame->m_bWrapLines)
1324 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1325 sLinenumber.Format(sLinenumberFormat, _T("·"));
1327 if (!sLinenumber.IsEmpty())
1329 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1330 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1332 pdc->SelectObject(GetFont());
1333 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1340 int CBaseView::GetMarginWidth()
1342 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1344 if (m_nDigits <= 0)
1346 int nLength = (int)m_pViewData->GetCount();
1347 // find out how many digits are needed to show the highest line number
1348 CString sMax;
1349 sMax.Format(_T("%d"), nLength);
1350 m_nDigits = sMax.GetLength();
1352 int nWidth = GetCharWidth();
1353 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1355 return MARGINWIDTH;
1358 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1360 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1361 COLORREF crBk, crFg;
1362 if (IsBottomViewGood())
1364 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1365 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1367 else
1369 DiffStates state = DIFFSTATE_REMOVED;
1370 if (this == m_pwndRight)
1372 state = DIFFSTATE_ADDED;
1374 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1376 pdc->SetBkColor(crBk);
1377 pdc->FillSolidRect(textrect, crBk);
1379 pdc->SetTextColor(crFg);
1381 pdc->SelectObject(GetFont(FALSE, TRUE));
1383 CString sViewTitle;
1384 if (IsModified())
1386 sViewTitle = _T("* ") + m_sWindowName;
1388 else
1390 sViewTitle = m_sWindowName;
1392 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1393 if (nStringLength > rect.Width())
1395 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1396 sViewTitle = m_sWindowName.Mid(offset);
1398 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1399 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1400 if (this->GetFocus() == this)
1401 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1402 else
1403 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1406 void CBaseView::OnDraw(CDC * pDC)
1408 CRect rcClient;
1409 GetClientRect(rcClient);
1411 int nLineCount = GetLineCount();
1412 int nLineHeight = GetLineHeight();
1414 CDC cacheDC;
1415 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1416 if (m_pCacheBitmap == NULL)
1418 m_pCacheBitmap = new CBitmap;
1419 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1421 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1423 DrawHeader(pDC, rcClient);
1425 CRect rcLine;
1426 rcLine = rcClient;
1427 rcLine.top += nLineHeight+HEADERHEIGHT;
1428 rcLine.bottom = rcLine.top + nLineHeight;
1429 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1430 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1432 int nCurrentLine = m_nTopLine;
1433 bool bBeyondFileLineCached = false;
1434 while (rcLine.top < rcClient.bottom)
1436 if (nCurrentLine < nLineCount)
1438 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1439 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1440 bBeyondFileLineCached = false;
1442 else if (!bBeyondFileLineCached)
1444 DrawMargin(&cacheDC, rcCacheMargin, -1);
1445 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1446 bBeyondFileLineCached = true;
1449 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1451 nCurrentLine ++;
1452 rcLine.OffsetRect(0, nLineHeight);
1455 cacheDC.SelectObject(pOldBitmap);
1456 cacheDC.DeleteDC();
1459 bool CBaseView::IsStateConflicted(DiffStates state)
1461 switch (state)
1463 case DIFFSTATE_CONFLICTED:
1464 case DIFFSTATE_CONFLICTED_IGNORED:
1465 case DIFFSTATE_CONFLICTEMPTY:
1466 case DIFFSTATE_CONFLICTADDED:
1467 return true;
1469 return false;
1472 bool CBaseView::IsStateEmpty(DiffStates state)
1474 switch (state)
1476 case DIFFSTATE_CONFLICTEMPTY:
1477 case DIFFSTATE_UNKNOWN:
1478 case DIFFSTATE_EMPTY:
1479 return true;
1481 return false;
1484 bool CBaseView::IsStateRemoved(DiffStates state)
1486 switch (state)
1488 case DIFFSTATE_REMOVED:
1489 case DIFFSTATE_THEIRSREMOVED:
1490 case DIFFSTATE_YOURSREMOVED:
1491 case DIFFSTATE_IDENTICALREMOVED:
1492 return true;
1494 return false;
1497 DiffStates CBaseView::ResolveState(DiffStates state)
1499 if (IsStateConflicted(state))
1501 if (state == DIFFSTATE_CONFLICTEMPTY)
1502 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1503 else
1504 return DIFFSTATE_CONFLICTRESOLVED;
1506 return state;
1510 bool CBaseView::IsLineEmpty(int nLineIndex)
1512 if (m_pViewData == 0)
1513 return FALSE;
1514 int nViewLine = GetViewLineForScreen(nLineIndex);
1515 return IsViewLineEmpty(nViewLine);
1518 bool CBaseView::IsViewLineEmpty(int nViewLine)
1520 if (m_pViewData == 0)
1521 return FALSE;
1522 const DiffStates state = m_pViewData->GetState(nViewLine);
1523 return IsStateEmpty(state);
1526 bool CBaseView::IsLineRemoved(int nLineIndex)
1528 if (m_pViewData == 0)
1529 return FALSE;
1530 int nViewLine = GetViewLineForScreen(nLineIndex);
1531 return IsViewLineRemoved(nViewLine);
1534 bool CBaseView::IsViewLineRemoved(int nViewLine)
1536 if (m_pViewData == 0)
1537 return FALSE;
1538 const DiffStates state = m_pViewData->GetState(nViewLine);
1539 return IsStateRemoved(state);
1542 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1544 if (m_pViewData == 0)
1545 return false;
1546 const DiffStates state = m_pViewData->GetState(nLineIndex);
1547 return IsStateConflicted(state);
1550 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1552 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1555 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1557 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1560 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1562 if (origin.x < (rc.left - GetCharWidth() +1))
1563 return;
1564 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1565 return;
1566 int viewLine = GetViewLineForScreen(nLineIndex);
1567 EOL ending = m_pViewData->GetLineEnding(viewLine);
1568 if (m_bIconLFs)
1570 HICON hEndingIcon = NULL;
1571 switch (ending)
1573 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1574 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1575 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1576 default: return;
1578 // If EOL style has changed, color end-of-line markers as inline differences.
1580 m_bShowInlineDiff && m_pOtherViewData &&
1581 (viewLine < m_pOtherViewData->GetCount()) &&
1582 (ending != EOL_NOENDING) &&
1583 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1584 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1587 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1590 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1592 else
1594 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1595 CPen * oldpen = pDC->SelectObject(&pen);
1596 int yMiddle = origin.y + rc.Height()/2;
1597 int xMiddle = origin.x+GetCharWidth()/2;
1598 bool bMultiline = false;
1599 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1601 if (GetLineLength(nLineIndex+1))
1603 // multiline
1604 bMultiline = true;
1605 pDC->MoveTo(origin.x, yMiddle-2);
1606 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1607 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1608 pDC->LineTo(origin.x, yMiddle+2);
1610 else if (GetLineLength(nLineIndex) == 0)
1611 bMultiline = true;
1613 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1614 bMultiline = true;
1616 if (!bMultiline)
1618 switch (ending)
1620 case EOL_AUTOLINE:
1621 case EOL_CRLF:
1622 // arrow from top to middle+2, then left
1623 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1624 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1625 case EOL_CR:
1626 // arrow from right to left
1627 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1628 pDC->LineTo(origin.x, yMiddle);
1629 pDC->LineTo(origin.x+4, yMiddle+4);
1630 pDC->MoveTo(origin.x, yMiddle);
1631 pDC->LineTo(origin.x+4, yMiddle-4);
1632 break;
1633 case EOL_LFCR:
1634 // from right-upper to left then down
1635 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1636 pDC->LineTo(xMiddle, yMiddle-2);
1637 pDC->LineTo(xMiddle, rc.bottom-1);
1638 pDC->LineTo(xMiddle+4, rc.bottom-5);
1639 pDC->MoveTo(xMiddle, rc.bottom-1);
1640 pDC->LineTo(xMiddle-4, rc.bottom-5);
1641 break;
1642 case EOL_LF:
1643 // arrow from top to bottom
1644 pDC->MoveTo(xMiddle, rc.top);
1645 pDC->LineTo(xMiddle, rc.bottom-1);
1646 pDC->LineTo(xMiddle+4, rc.bottom-5);
1647 pDC->MoveTo(xMiddle, rc.bottom-1);
1648 pDC->LineTo(xMiddle-4, rc.bottom-5);
1649 break;
1650 case EOL_FF: // Form Feed, U+000C
1651 case EOL_NEL: // Next Line, U+0085
1652 case EOL_LS: // Line Separator, U+2028
1653 case EOL_PS: // Paragraph Separator, U+2029
1654 // draw a horizontal line at the bottom of this line
1655 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1656 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1657 pDC->LineTo(origin.x, rc.bottom-2);
1658 pDC->LineTo(origin.x+5, rc.bottom-2);
1659 pDC->MoveTo(origin.x, rc.bottom-2);
1660 pDC->LineTo(origin.x+1, rc.bottom-6);
1661 break;
1662 default: // other EOLs
1663 // arrow from top right to bottom left
1664 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1665 pDC->LineTo(origin.x, rc.bottom-1);
1666 pDC->LineTo(origin.x+5, rc.bottom-2);
1667 pDC->MoveTo(origin.x, rc.bottom-1);
1668 pDC->LineTo(origin.x+1, rc.bottom-6);
1669 break;
1670 case EOL_NOENDING:
1671 break;
1674 pDC->SelectObject(oldpen);
1678 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1680 if (!m_bShowSelection)
1681 return;
1683 int nSelBlockStart;
1684 int nSelBlockEnd;
1685 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1686 return;
1688 const int THICKNESS = 2;
1689 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1691 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1692 int nSubLine = GetSubLineOffset(nLineIndex);
1693 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1694 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1696 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1699 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1700 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1702 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1706 void CBaseView::DrawTextLine(
1707 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1709 ASSERT(nLineIndex < GetLineCount());
1710 int nViewLine = GetViewLineForScreen(nLineIndex);
1711 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1713 LineColors lineCols = GetLineColors(nViewLine);
1715 CString sViewLine = GetViewLineChars(nViewLine);
1716 // mark selection
1717 if (m_bShowSelection && HasTextSelection())
1719 // has this line selection ?
1720 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1722 int nViewLineLength = sViewLine.GetLength();
1724 // first suppose the whole line is selected
1725 int selectedStart = 0;
1726 int selectedEnd = nViewLineLength;
1728 // the view line is partially selected
1729 if (m_ptSelectionViewPosStart.y == nViewLine)
1731 selectedStart = m_ptSelectionViewPosStart.x;
1734 if (m_ptSelectionViewPosEnd.y == nViewLine)
1736 selectedEnd = m_ptSelectionViewPosEnd.x;
1738 // apply selection coloring
1739 // First enforce start and end point
1740 lineCols.SplitBlock(selectedStart);
1741 lineCols.SplitBlock(selectedEnd);
1742 // change color of affected parts
1743 long intenseColorScale = m_bFocused ? 70 : 30;
1744 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1745 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1747 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1748 if (it->second.shot == it->second.background)
1750 it->second.shot = crBk;
1752 it->second.background = crBk;
1753 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1758 // TODO: remove duplicate from selection and mark
1759 if (!m_sMarkedWord.IsEmpty())
1761 int nMarkLength = m_sMarkedWord.GetLength();
1762 //int nViewLineLength = sViewLine.GetLength();
1763 const TCHAR * text = sViewLine;
1764 const TCHAR * findText = text;
1765 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1767 int nMarkStart = static_cast<int>(findText - text);
1768 int nMarkEnd = nMarkStart + nMarkLength;
1769 findText += nMarkLength;
1770 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1771 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1772 if (eLeft == eStart)
1773 continue;
1774 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1775 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1776 if (eRight == eEnd)
1777 continue;
1779 // First enforce start and end point
1780 lineCols.SplitBlock(nMarkStart);
1781 lineCols.SplitBlock(nMarkEnd);
1782 // change color of affected parts
1783 const long int nIntenseColorScale = 200;
1784 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1785 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1787 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1788 if (it->second.shot == it->second.background)
1790 it->second.shot = crBk;
1792 it->second.background = crBk;
1793 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1797 if (!m_sFindText.IsEmpty())
1799 int nMarkStart = 0;
1800 int nMarkEnd = 0;
1801 int nStringPos = nMarkStart;
1802 CString searchLine = sViewLine;
1803 if (!m_bMatchCase)
1804 searchLine.MakeLower();
1805 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1807 // First enforce start and end point
1808 lineCols.SplitBlock(nMarkStart+nStringPos);
1809 lineCols.SplitBlock(nMarkEnd+nStringPos);
1810 // change color of affected parts
1811 const long int nIntenseColorScale = 30;
1812 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1813 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1815 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1816 if (it->second.shot == it->second.background)
1818 it->second.shot = crBk;
1820 it->second.background = crBk;
1821 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1823 searchLine = searchLine.Mid(nMarkEnd);
1824 nStringPos = nMarkEnd;
1825 nMarkStart = 0;
1826 nMarkEnd = 0;
1830 // @ this point we may cache data for next line which may be same in wrapped mode
1832 int nTextOffset = 0;
1833 int nSubline = GetSubLineOffset(nLineIndex);
1834 for (int n=0; n<nSubline; n++)
1836 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1837 nTextOffset += sLine.GetLength();
1840 CString sLine = GetLineChars(nLineIndex);
1841 int nLineLength = sLine.GetLength();
1842 CString sLineExp = ExpandChars(sLine);
1843 LPCTSTR textExp = sLineExp;
1844 //int nLineLengthExp = sLineExp.GetLength();
1845 int nStartExp = 0;
1846 int nLeft = coords.x;
1847 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1849 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1850 ++itEnd;
1851 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1852 int nEnd = nLineLength;
1853 if (itEnd != lineCols.end())
1855 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1857 int nBlockLength = nEnd - nStart;
1858 if (nBlockLength > 0 && nEnd>=0)
1860 pDC->SetBkColor(itStart->second.background);
1861 pDC->SetTextColor(itStart->second.text);
1862 int nEndExp = CountExpandedChars(sLine, nEnd);
1863 int nTextLength = nEndExp - nStartExp;
1864 LPCTSTR p_zBlockText = textExp + nStartExp;
1865 SIZE Size;
1866 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1867 int nRight = nLeft + Size.cx;
1868 if ((nRight > rc.left) && (nLeft < rc.right))
1870 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1871 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1872 // is 4094 (4095 doesn't work anymore).
1873 // So we limit the length here to that 4094 chars.
1874 // In case we're scrolled to the right, there's no need to draw the string
1875 // from way outside our window, so we also offset the drawing to the start of the window.
1876 // This reduces the string length as well.
1877 int offset = 0;
1878 int leftcoord = nLeft;
1879 if (nLeft < 0)
1881 int fit = nTextLength;
1882 std::unique_ptr<int> posBuffer(new int[fit]);
1883 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1884 int lower = 0, upper = fit - 1;
1887 int middle = (upper + lower + 1) / 2;
1888 int width = posBuffer.get()[middle];
1889 if (rc.left - nLeft < width)
1890 upper = middle - 1;
1891 else
1892 lower = middle;
1893 } while (lower < upper);
1895 offset = lower;
1896 nTextLength -= offset;
1897 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1900 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1901 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1903 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1906 nLeft = nRight;
1907 coords.x = nRight;
1908 nStartExp = nEndExp;
1913 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1915 if (nLineIndex >= GetLineCount())
1916 nLineIndex = -1;
1917 ASSERT(nLineIndex >= -1);
1919 if ((nLineIndex == -1) || !m_pViewData)
1921 // Draw line beyond the text
1922 COLORREF crBkgnd, crText;
1923 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1924 pDC->FillSolidRect(rc, crBkgnd);
1925 return;
1928 int viewLine = GetViewLineForScreen(nLineIndex);
1929 if (m_pMainFrame->m_bCollapsed)
1931 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1933 COLORREF crBkgnd, crText;
1934 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1935 pDC->FillSolidRect(rc, crBkgnd);
1937 const int THICKNESS = 2;
1938 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1939 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1940 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1941 pDC->SetBkColor(crBkgnd);
1942 CRect rect = rc;
1943 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1944 return;
1948 DiffStates diffState = m_pViewData->GetState(viewLine);
1949 COLORREF crBkgnd, crText;
1950 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1952 if (diffState == DIFFSTATE_CONFLICTED)
1954 // conflicted lines are shown without 'text' on them
1955 CRect rect = rc;
1956 pDC->FillSolidRect(rc, crBkgnd);
1957 // now draw some faint text patterns
1958 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1959 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1960 DrawBlockLine(pDC, rc, nLineIndex);
1961 return;
1964 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1965 CString sLine = GetLineChars(nLineIndex);
1966 if (sLine.IsEmpty())
1968 pDC->FillSolidRect(rc, crBkgnd);
1969 DrawBlockLine(pDC, rc, nLineIndex);
1970 DrawLineEnding(pDC, rc, nLineIndex, origin);
1971 return;
1974 CheckOtherView();
1976 // Draw the line
1978 pDC->SelectObject(GetFont(FALSE, FALSE));
1980 DrawTextLine(pDC, rc, nLineIndex, origin);
1982 // draw white space after the end of line
1983 CRect frect = rc;
1984 if (origin.x > frect.left)
1985 frect.left = origin.x;
1986 if (frect.right > frect.left)
1987 pDC->FillSolidRect(frect, crBkgnd);
1989 // draw the whitespace chars
1990 LPCTSTR pszChars = (LPCWSTR)sLine;
1991 if (m_bViewWhitespace)
1993 int xpos = 0;
1994 int nChars = 0;
1995 LPCTSTR pLastSpace = pszChars;
1996 int y = rc.top + (rc.bottom-rc.top)/2;
1997 xpos -= m_nOffsetChar * GetCharWidth();
1999 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2000 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
2001 while (*pszChars)
2003 switch (*pszChars)
2005 case '\t':
2007 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2008 pLastSpace = pszChars + 1;
2009 // draw an arrow
2010 int nSpaces = GetTabSize() - nChars % GetTabSize();
2011 if (xpos + nSpaces * GetCharWidth() > 0)
2013 int xposreal = max(xpos, 0);
2014 if ((xposreal > 0) || (nSpaces > 0))
2016 CPen * oldPen = pDC->SelectObject(&pen);
2017 pDC->MoveTo(xposreal + rc.left, y);
2018 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2019 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y - 4);
2020 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2021 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y + 4);
2022 pDC->SelectObject(oldPen);
2025 xpos += nSpaces * GetCharWidth();
2026 nChars += nSpaces;
2028 break;
2029 case ' ':
2031 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2032 pLastSpace = pszChars + 1;
2033 // draw a small dot
2034 if (xpos >= 0)
2036 CPen * oldPen = pDC->SelectObject(&pen2);
2037 pDC->MoveTo(xpos + rc.left + GetCharWidth()/2-1, y);
2038 pDC->LineTo(xpos + rc.left + GetCharWidth()/2+1, y);
2039 pDC->SelectObject(oldPen);
2041 xpos += GetCharWidth();
2042 nChars++;
2044 break;
2045 default:
2046 nChars++;
2047 break;
2049 pszChars++;
2052 DrawBlockLine(pDC, rc, nLineIndex);
2053 if (origin.x >= rc.left)
2054 DrawLineEnding(pDC, rc, nLineIndex, origin);
2057 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2059 if (nCount <= 0)
2061 line = _T("");
2062 return;
2065 int nTabSize = GetTabSize();
2067 int nActualOffset = CountExpandedChars(sLine, nOffset);
2069 LPCTSTR pszChars = (LPCWSTR)sLine;
2070 pszChars += nOffset;
2071 int nLength = nCount;
2073 int nTabCount = 0;
2074 for (int i=0; i<nLength; i++)
2076 if (pszChars[i] == _T('\t'))
2077 nTabCount ++;
2080 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2081 int nCurPos = 0;
2082 if (nTabCount > 0 || m_bViewWhitespace)
2084 for (int i=0; i<nLength; i++)
2086 if (pszChars[i] == _T('\t'))
2088 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2089 while (nSpaces > 0)
2091 pszBuf[nCurPos ++] = _T(' ');
2092 nSpaces --;
2095 else
2097 pszBuf[nCurPos] = pszChars[i];
2098 nCurPos ++;
2102 else
2104 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2105 nCurPos = nLength;
2107 pszBuf[nCurPos] = 0;
2108 line.ReleaseBuffer();
2111 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2113 CString sRet;
2114 int nLength = sLine.GetLength();
2115 ExpandChars(sLine, nOffset, nLength, sRet);
2116 return sRet;
2119 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2121 int nTabSize = GetTabSize();
2123 int nActualOffset = 0;
2124 for (int i=0; i<nLength; i++)
2126 if (sLine[i] == _T('\t'))
2127 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2128 else
2129 nActualOffset ++;
2131 return nActualOffset;
2134 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2136 if (m_pwndLeft)
2137 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2138 if (m_pwndRight)
2139 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2140 if (m_pwndBottom)
2141 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2142 if (m_pwndLocator)
2143 m_pwndLocator->Invalidate();
2146 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2148 //almost the same as ScrollAllToLine, but try to put the line in the
2149 //middle of the view, not on top
2150 int nNewTopLine = nNewLine - GetScreenLines()/2;
2151 if (nNewTopLine < 0)
2152 nNewTopLine = 0;
2153 if (nNewTopLine >= (int)m_Screen2View.size())
2154 nNewTopLine = (int)m_Screen2View.size()-1;
2155 if (bAll)
2156 ScrollAllToLine(nNewTopLine);
2157 else
2158 ScrollToLine(nNewTopLine);
2161 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2163 return TRUE;
2166 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2168 if (CView::OnCreate(lpCreateStruct) == -1)
2169 return -1;
2171 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2172 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2173 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2174 m_lfBaseFont.lfHeight = 0;
2175 m_lfBaseFont.lfWeight = FW_NORMAL;
2176 m_lfBaseFont.lfItalic = FALSE;
2177 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2178 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2179 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2180 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2181 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2183 return 0;
2186 void CBaseView::OnDestroy()
2188 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2190 m_pFindDialog->SendMessage(WM_CLOSE);
2191 return;
2193 CView::OnDestroy();
2194 DeleteFonts();
2195 ReleaseBitmap();
2198 void CBaseView::OnSize(UINT nType, int cx, int cy)
2200 CView::OnSize(nType, cx, cy);
2201 ReleaseBitmap();
2203 m_nScreenLines = -1;
2204 m_nScreenChars = -1;
2205 if (m_nLastScreenChars != GetScreenChars())
2207 BuildAllScreen2ViewVector();
2208 m_nLastScreenChars = m_nScreenChars;
2209 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2211 // if we're in wrap mode, the line wrapping most likely changed
2212 // and that means we have to redraw the whole window, not just the
2213 // scrolled part.
2214 Invalidate(FALSE);
2216 else
2218 // make sure the view header is redrawn
2219 CRect rcScroll;
2220 GetClientRect(&rcScroll);
2221 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2222 InvalidateRect(&rcScroll, FALSE);
2225 else
2227 // make sure the view header is redrawn
2228 CRect rcScroll;
2229 GetClientRect(&rcScroll);
2230 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2231 InvalidateRect(&rcScroll, FALSE);
2233 UpdateLocator();
2234 RecalcVertScrollBar();
2235 RecalcHorzScrollBar();
2237 UpdateCaret();
2240 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2242 if (m_pwndLeft)
2243 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2244 if (m_pwndRight)
2245 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2246 if (m_pwndBottom)
2247 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2248 if (m_pwndLocator)
2249 m_pwndLocator->Invalidate();
2250 return CView::OnMouseWheel(nFlags, zDelta, pt);
2253 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2255 if (m_pwndLeft)
2256 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2257 if (m_pwndRight)
2258 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2259 if (m_pwndBottom)
2260 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2261 if (m_pwndLocator)
2262 m_pwndLocator->Invalidate();
2265 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2267 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2268 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2270 if (bControl || bShift)
2272 if (m_pMainFrame->m_bWrapLines)
2273 return;
2274 // Ctrl-Wheel scrolls sideways
2275 ScrollSide(-zDelta/30);
2277 else
2279 ScrollVertical(zDelta);
2283 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2285 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2286 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2288 if (bControl || bShift)
2290 ScrollVertical(zDelta);
2292 else
2294 if (m_pMainFrame->m_bWrapLines)
2295 return;
2296 // Ctrl-Wheel scrolls sideways
2297 ScrollSide(-zDelta/30);
2301 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2303 if (nHitTest == HTCLIENT)
2305 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2307 if (m_nMouseLine < (int)m_Screen2View.size())
2309 if (m_nMouseLine >= 0)
2311 int viewLine = GetViewLineForScreen(m_nMouseLine);
2312 if (viewLine < m_pViewData->GetCount())
2314 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2316 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2317 return TRUE;
2323 if (m_mouseInMargin)
2325 ::SetCursor(m_margincursor);
2326 return TRUE;
2328 if (m_nMouseLine >= 0)
2330 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2331 return TRUE;
2334 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2335 return TRUE;
2337 return CView::OnSetCursor(pWnd, nHitTest, message);
2340 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2342 CView::OnKillFocus(pNewWnd);
2343 m_bFocused = FALSE;
2344 UpdateCaret();
2345 Invalidate();
2348 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2350 CView::OnSetFocus(pOldWnd);
2351 m_bFocused = TRUE;
2352 UpdateCaret();
2353 Invalidate();
2356 int CBaseView::GetLineFromPoint(CPoint point)
2358 ScreenToClient(&point);
2359 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2362 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2364 if (!this->IsWindowVisible())
2365 return;
2367 CIconMenu popup;
2368 if (!popup.CreatePopupMenu())
2369 return;
2371 AddContextItems(popup, state);
2373 CMenu popupEols;
2374 CMenu popupUnicode;
2375 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2376 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2377 if (IsWritable())
2379 CString temp;
2380 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2381 temp.LoadString(IDS_EDIT_TAB2SPACE);
2382 popup.AppendMenu((MF_STRING | oWhites.HasTabsToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2383 temp.LoadString(IDS_EDIT_SPACE2TAB);
2384 popup.AppendMenu((MF_STRING | oWhites.HasSpacesToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2385 temp.LoadString(IDS_EDIT_TRIM);
2386 popup.AppendMenu((MF_STRING | oWhites.HasTrailWhiteChars) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2388 // add eol submenu
2389 if (!popupEols.CreatePopupMenu())
2390 return;
2392 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2393 for (int i = 1; i < _countof(eolArray); i++)
2395 CString temp = GetEolName(eolArray[i]);
2396 bool bChecked = (eEolType == eolArray[i]);
2397 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2400 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2401 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2403 // add encoding submenu
2404 if (!popupUnicode.CreatePopupMenu())
2405 return;
2406 for (int i = 0; i < _countof(uctArray); i++)
2408 CString temp = CFileTextLines::GetEncodingName(uctArray[i]);
2409 bool bChecked = (m_texttype == uctArray[i]);
2410 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2412 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2413 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2417 CompensateForKeyboard(point);
2419 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2420 ResetUndoStep();
2421 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2423 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2425 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2427 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2428 SaveUndoStep();
2430 switch (cmd)
2432 // 2-pane view commands; target is right view
2433 case POPUPCOMMAND_USELEFTBLOCK:
2434 m_pwndRight->UseLeftBlock();
2435 break;
2436 case POPUPCOMMAND_USELEFTFILE:
2437 m_pwndRight->UseLeftFile();
2438 break;
2439 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2440 m_pwndRight->UseBothLeftFirst();
2441 break;
2442 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2443 m_pwndRight->UseBothRightFirst();
2444 break;
2445 case POPUPCOMMAND_MARKBLOCK:
2446 m_pwndRight->MarkBlock(true);
2447 break;
2448 case POPUPCOMMAND_UNMARKBLOCK:
2449 m_pwndRight->MarkBlock(false);
2450 break;
2451 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2452 m_pwndRight->LeaveOnlyMarkedBlocks();
2453 break;
2454 // 2-pane view multiedit commands; target is left view
2455 case POPUPCOMMAND_PREPENDFROMRIGHT:
2456 if (!m_pwndLeft->IsReadonly())
2457 m_pwndLeft->UseBothRightFirst();
2458 break;
2459 case POPUPCOMMAND_REPLACEBYRIGHT:
2460 if (!m_pwndLeft->IsReadonly())
2461 m_pwndLeft->UseRightBlock();
2462 break;
2463 case POPUPCOMMAND_APPENDFROMRIGHT:
2464 if (!m_pwndLeft->IsReadonly())
2465 m_pwndLeft->UseBothLeftFirst();
2466 break;
2467 case POPUPCOMMAND_USERIGHTFILE:
2468 m_pwndLeft->UseRightFile();
2469 break;
2470 // 3-pane view commands; target is bottom view
2471 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2472 m_pwndBottom->UseBothRightFirst();
2473 break;
2474 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2475 m_pwndBottom->UseBothLeftFirst();
2476 break;
2477 case POPUPCOMMAND_USEYOURBLOCK:
2478 m_pwndBottom->UseRightBlock();
2479 break;
2480 case POPUPCOMMAND_USEYOURFILE:
2481 m_pwndBottom->UseRightFile();
2482 break;
2483 case POPUPCOMMAND_USETHEIRBLOCK:
2484 m_pwndBottom->UseLeftBlock();
2485 break;
2486 case POPUPCOMMAND_USETHEIRFILE:
2487 m_pwndBottom->UseLeftFile();
2488 break;
2489 // copy, cut and paste commands
2490 case ID_EDIT_COPY:
2491 OnEditCopy();
2492 break;
2493 case ID_EDIT_CUT:
2494 OnEditCut();
2495 break;
2496 case ID_EDIT_PASTE:
2497 OnEditPaste();
2498 break;
2499 // white chars manipulations
2500 case POPUPCOMMAND_TABTOSPACES:
2501 ConvertTabToSpaces();
2502 break;
2503 case POPUPCOMMAND_SPACESTOTABS:
2504 Tabularize();
2505 break;
2506 case POPUPCOMMAND_REMOVETRAILWHITES:
2507 RemoveTrailWhiteChars();
2508 break;
2509 default:
2510 return;
2511 } // switch (cmd)
2512 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2513 return;
2516 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2518 if (!m_pViewData)
2519 return;
2521 int nViewBlockStart = -1;
2522 int nViewBlockEnd = -1;
2523 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2524 if ((point.x >= 0) && (point.y >= 0))
2526 int nLine = GetLineFromPoint(point)-1;
2527 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2529 int nViewLine = GetViewLineForScreen(nLine);
2530 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2532 ClearSelection(); // Clear text-copy selection
2534 nViewBlockStart = nViewLine;
2535 nViewBlockEnd = nViewLine;
2536 DiffStates state = m_pViewData->GetState(nViewLine);
2537 while (nViewBlockStart > 0)
2539 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2540 if (!LinesInOneChange(-1, state, lineState))
2541 break;
2542 nViewBlockStart--;
2545 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2547 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2548 if (!LinesInOneChange(1, state, lineState))
2549 break;
2550 nViewBlockEnd++;
2553 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2554 UpdateCaretPosition(point);
2559 // FixSelection(); fix selection range
2560 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2561 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2563 DiffStates state = DIFFSTATE_UNKNOWN;
2564 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2566 // find a more 'relevant' state in the selection
2567 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2569 state = m_pViewData->GetState(i);
2570 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2571 break;
2574 OnContextMenu(point, state);
2577 void CBaseView::RefreshViews()
2579 if (m_pwndLeft)
2581 m_pwndLeft->UpdateStatusBar();
2582 m_pwndLeft->Invalidate();
2584 if (m_pwndRight)
2586 m_pwndRight->UpdateStatusBar();
2587 m_pwndRight->Invalidate();
2589 if (m_pwndBottom)
2591 m_pwndBottom->UpdateStatusBar();
2592 m_pwndBottom->Invalidate();
2594 if (m_pwndLocator)
2595 m_pwndLocator->Invalidate();
2598 void CBaseView::GoToFirstDifference()
2600 SetCaretToFirstViewLine();
2601 SelectNextBlock(1, false, false);
2604 void CBaseView::GoToFirstConflict()
2606 SetCaretToFirstViewLine();
2607 SelectNextBlock(1, true, false);
2610 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2612 ClearSelection();
2613 SetupAllSelection(nStart, max(nStart, nEnd));
2615 UpdateCaretPosition(SetupPoint(0, nStart));
2616 Invalidate();
2619 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2621 ClearSelection();
2622 SetupAllViewSelection(nStart, max(nStart, nEnd));
2624 UpdateCaretViewPosition(SetupPoint(0, nStart));
2625 Invalidate();
2628 void CBaseView::SetupAllViewSelection(int start, int end)
2630 SetupViewSelection(m_pwndBottom, start, end);
2631 SetupViewSelection(m_pwndLeft, start, end);
2632 SetupViewSelection(m_pwndRight, start, end);
2635 void CBaseView::SetupAllSelection(int start, int end)
2637 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2640 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2642 void CBaseView::SetupSelection(int start, int end)
2644 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2647 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2649 if (!IsViewGood(view))
2650 return;
2651 view->SetupViewSelection(start, end);
2654 void CBaseView::SetupViewSelection(int start, int end)
2656 // clear text selection before setting line selection ?
2657 m_nSelViewBlockStart = start;
2658 m_nSelViewBlockEnd = end;
2659 Invalidate();
2663 void CBaseView::OnMergePreviousconflict()
2665 SelectNextBlock(-1, true);
2668 void CBaseView::OnMergeNextconflict()
2670 SelectNextBlock(1, true);
2673 void CBaseView::OnMergeNextdifference()
2675 SelectNextBlock(1, false);
2678 void CBaseView::OnMergePreviousdifference()
2680 SelectNextBlock(-1, false);
2683 bool CBaseView::HasNextConflict()
2685 return SelectNextBlock(1, true, true, true);
2688 bool CBaseView::HasPrevConflict()
2690 return SelectNextBlock(-1, true, true, true);
2693 bool CBaseView::HasNextDiff()
2695 return SelectNextBlock(1, false, true, true);
2698 bool CBaseView::HasPrevDiff()
2700 return SelectNextBlock(-1, false, true, true);
2703 bool CBaseView::LinesInOneChange(int direction,
2704 DiffStates initialLineState, DiffStates currentLineState)
2706 // Checks whether all the adjacent lines starting from the initial line
2707 // and up to the current line form the single change
2709 // First of all, if the two lines have identical states, they surely
2710 // belong to one change.
2711 if (initialLineState == currentLineState)
2712 return true;
2714 // Either we move down and initial line state is "added" or "removed" and
2715 // current line state is "empty"...
2716 if (direction > 0)
2718 if (currentLineState == DIFFSTATE_EMPTY)
2720 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2721 return true;
2723 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2724 return true;
2726 // ...or we move up and initial line state is "empty" and current line
2727 // state is "added" or "removed".
2728 if (direction < 0)
2730 if (initialLineState == DIFFSTATE_EMPTY)
2732 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2733 return true;
2735 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2736 return true;
2738 return false;
2741 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2743 if (! m_pViewData)
2744 return false;
2746 const int linesCount = (int)m_Screen2View.size();
2747 if(linesCount == 0)
2748 return false;
2750 int nCenterPos = GetCaretPosition().y;
2751 int nLimit = -1;
2752 if (nDirection > 0)
2753 nLimit = linesCount;
2755 if (nCenterPos >= linesCount)
2756 nCenterPos = linesCount-1;
2758 if (bSkipEndOfCurrentBlock)
2760 // Find end of current block
2761 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2762 while (nCenterPos != nLimit)
2764 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2765 if (!LinesInOneChange(nDirection, state, lineState))
2766 break;
2767 nCenterPos += nDirection;
2771 // Find next diff/conflict block
2772 while (nCenterPos != nLimit)
2774 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2775 if (!bConflict &&
2776 (linestate != DIFFSTATE_NORMAL) &&
2777 (linestate != DIFFSTATE_UNKNOWN))
2779 break;
2781 if (bConflict &&
2782 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2783 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2784 (linestate == DIFFSTATE_CONFLICTED) ||
2785 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2787 break;
2790 nCenterPos += nDirection;
2792 if (nCenterPos == nLimit)
2793 return false;
2794 if (dryrun)
2795 return (nCenterPos != nLimit);
2797 // Find end of new block
2798 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2799 int nBlockEnd = nCenterPos;
2800 const int maxAllowedLine = nLimit-nDirection;
2801 while (nBlockEnd != maxAllowedLine)
2803 const int lineIndex = nBlockEnd + nDirection;
2804 if (lineIndex >= linesCount)
2805 break;
2806 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2807 if (!LinesInOneChange(nDirection, state, lineState))
2808 break;
2809 nBlockEnd += nDirection;
2812 int nTopPos = nCenterPos - (GetScreenLines()/2);
2813 if (nTopPos < 0)
2814 nTopPos = 0;
2816 POINT ptCaretPos = {0, nCenterPos};
2817 SetCaretPosition(ptCaretPos);
2818 ClearSelection();
2819 if (nDirection > 0)
2820 SetupAllSelection(nCenterPos, nBlockEnd);
2821 else
2822 SetupAllSelection(nBlockEnd, nCenterPos);
2824 ScrollAllToLine(nTopPos, FALSE);
2825 RecalcAllVertScrollBars(TRUE);
2826 SetCaretToLineStart();
2827 EnsureCaretVisible();
2828 OnNavigateNextinlinediff();
2830 UpdateViewsCaretPosition();
2831 UpdateCaret();
2832 ShowDiffLines(nCenterPos);
2833 return true;
2836 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2838 if (pNMHDR->idFrom != (UINT)m_hWnd)
2839 return FALSE;
2841 CString strTipText;
2842 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2844 DWORD pos = GetMessagePos();
2845 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2846 ScreenToClient(&point);
2847 const int nLine = GetButtonEventLineIndex(point);
2849 if (nLine >= 0)
2851 int nViewLine = GetViewLineForScreen(nLine);
2852 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2854 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2855 if (movedIndex >= 0)
2857 if (m_pViewData->IsMovedFrom(nViewLine))
2859 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2861 else
2863 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2870 *pResult = 0;
2871 if (strTipText.IsEmpty())
2872 return TRUE;
2874 // need to handle both ANSI and UNICODE versions of the message
2875 if (pNMHDR->code == TTN_NEEDTEXTA)
2877 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2878 pTTTA->lpszText = m_szTip;
2879 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2881 else
2883 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2884 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2885 pTTTW->lpszText = m_wszTip;
2888 return TRUE; // message was handled
2891 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2893 CRect rcClient;
2894 GetClientRect(rcClient);
2895 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2896 int marginwidth = MARGINWIDTH;
2897 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2899 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2901 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2903 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2905 // inside the header part of the view (showing the filename)
2906 pTI->hwnd = this->m_hWnd;
2907 this->GetClientRect(&pTI->rect);
2908 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2909 pTI->uId = (UINT)m_hWnd;
2910 pTI->lpszText = LPSTR_TEXTCALLBACK;
2912 // we want multi line tooltips
2913 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2914 if (pToolTip->GetSafeHwnd() != NULL)
2916 pToolTip->SetMaxTipWidth(INT_MAX);
2919 return (textrect.PtInRect(point) ? 1 : 2);
2922 return -1;
2925 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2927 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2928 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2930 switch (nChar)
2932 case VK_TAB:
2933 if (bControl)
2935 if (this==m_pwndLeft)
2937 if (IsViewGood(m_pwndRight))
2939 m_pwndRight->SetFocus();
2941 else if (IsViewGood(m_pwndBottom))
2943 m_pwndBottom->SetFocus();
2946 else if (this==m_pwndRight)
2948 if (IsViewGood(m_pwndBottom))
2950 m_pwndBottom->SetFocus();
2952 else if (IsViewGood(m_pwndLeft))
2954 m_pwndLeft->SetFocus();
2957 else if (this==m_pwndBottom)
2959 if (IsViewGood(m_pwndLeft))
2961 m_pwndLeft->SetFocus();
2963 else if (IsViewGood(m_pwndRight))
2965 m_pwndRight->SetFocus();
2969 break;
2970 case VK_PRIOR:
2972 POINT ptCaretPos = GetCaretPosition();
2973 ptCaretPos.y -= GetScreenLines();
2974 ptCaretPos.y = max(ptCaretPos.y, 0);
2975 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2976 SetCaretPosition(ptCaretPos);
2977 OnCaretMove(MOVELEFT, bShift);
2978 ShowDiffLines(ptCaretPos.y);
2980 break;
2981 case VK_NEXT:
2983 POINT ptCaretPos = GetCaretPosition();
2984 ptCaretPos.y += GetScreenLines();
2985 if (ptCaretPos.y >= GetLineCount())
2986 ptCaretPos.y = GetLineCount()-1;
2987 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2988 SetCaretPosition(ptCaretPos);
2989 OnCaretMove(MOVERIGHT, bShift);
2990 ShowDiffLines(ptCaretPos.y);
2992 break;
2993 case VK_HOME:
2995 if (bControl)
2997 ScrollAllToLine(0);
2998 SetCaretToViewStart();
2999 m_nCaretGoalPos = 0;
3000 if (bShift)
3001 AdjustSelection(MOVELEFT);
3002 else
3003 ClearSelection();
3004 UpdateCaret();
3006 else
3008 POINT ptCaretPos = GetCaretPosition();
3009 CString sLine = GetLineChars(ptCaretPos.y);
3010 int pos = 0;
3011 while (pos < sLine.GetLength())
3013 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3014 break;
3015 ++pos;
3017 if (ptCaretPos.x == pos)
3019 SetCaretToLineStart();
3020 m_nCaretGoalPos = 0;
3021 OnCaretMove(MOVERIGHT, bShift);
3022 ScrollAllToChar(0);
3024 else
3026 ptCaretPos.x = pos;
3027 SetCaretAndGoalPosition(ptCaretPos);
3028 OnCaretMove(MOVELEFT, bShift);
3032 break;
3033 case VK_END:
3035 if (bControl)
3037 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3038 POINT ptCaretPos;
3039 ptCaretPos.y = GetLineCount()-1;
3040 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3041 SetCaretAndGoalPosition(ptCaretPos);
3042 if (bShift)
3043 AdjustSelection(MOVERIGHT);
3044 else
3045 ClearSelection();
3047 else
3049 POINT ptCaretPos = GetCaretPosition();
3050 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3051 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3053 ptCaretPos.x--;
3055 SetCaretAndGoalPosition(ptCaretPos);
3056 OnCaretMove(MOVERIGHT, bShift);
3059 break;
3060 case VK_BACK:
3061 if (IsWritable())
3063 if (! HasTextSelection())
3065 POINT ptCaretPos = GetCaretPosition();
3066 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3067 break;
3068 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3069 if (bControl)
3070 MoveCaretWordLeft();
3071 else
3073 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3077 m_ptSelectionViewPosStart = GetCaretViewPosition();
3079 RemoveSelectedText();
3081 break;
3082 case VK_DELETE:
3083 if (IsWritable())
3085 if (! HasTextSelection())
3087 if (bControl)
3089 m_ptSelectionViewPosStart = GetCaretViewPosition();
3090 MoveCaretWordRight();
3091 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3093 else
3095 if (! MoveCaretRight())
3096 break;
3097 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3098 MoveCaretLeft();
3099 m_ptSelectionViewPosStart = GetCaretViewPosition();
3102 RemoveSelectedText();
3104 break;
3105 case VK_INSERT:
3106 m_bInsertMode = !m_bInsertMode;
3107 UpdateCaret();
3108 break;
3110 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3113 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3115 const int nClickedLine = GetButtonEventLineIndex(point);
3116 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3118 POINT ptCaretPos;
3119 ptCaretPos.y = nClickedLine;
3120 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3121 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3122 SetCaretAndGoalPosition(ptCaretPos);
3124 if (nFlags & MK_SHIFT)
3125 AdjustSelection(MOVERIGHT);
3126 else
3128 ClearSelection();
3129 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3130 if (point.x < GetMarginWidth())
3132 // select the whole line
3133 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3134 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3138 UpdateViewsCaretPosition();
3139 Invalidate();
3142 CView::OnLButtonDown(nFlags, point);
3145 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3147 if (zChar == ' ' || zChar == '\t' )
3149 return CHG_WHITESPACE;
3151 if (zChar < 0x20)
3153 return CHG_CONTROL;
3155 if (m_sWordSeparators.Find(zChar) >= 0)
3157 return CHG_WORDSEPARATOR;
3159 return CHG_WORDLETTER;
3162 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3164 if (m_pViewData == 0) {
3165 CView::OnLButtonDblClk(nFlags, point);
3166 return;
3169 const int nClickedLine = GetButtonEventLineIndex(point);
3170 if ( nClickedLine < 0)
3171 return;
3172 int nViewLine = GetViewLineForScreen(nClickedLine);
3173 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3175 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3177 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3179 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3180 int screenLine = FindViewLineNumber(movedindex);
3181 int nTop = screenLine - GetScreenLines()/2;
3182 if (nTop < 0)
3183 nTop = 0;
3184 ScrollAllToLine(nTop);
3185 // find and select the whole moved block
3186 int startSel = movedindex;
3187 int endSel = movedindex;
3188 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3189 startSel--;
3190 startSel++;
3191 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3192 endSel++;
3193 endSel--;
3194 m_pOtherView->SetupSelection(startSel, endSel);
3195 return CView::OnLButtonDblClk(nFlags, point);
3199 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3201 // a double click on a marker expands the hidden text
3202 int i = nViewLine;
3203 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3205 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3206 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3207 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3208 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3209 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3210 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3211 i++;
3213 BuildAllScreen2ViewVector();
3214 if (m_pwndLeft)
3215 m_pwndLeft->Invalidate();
3216 if (m_pwndRight)
3217 m_pwndRight->Invalidate();
3218 if (m_pwndBottom)
3219 m_pwndBottom->Invalidate();
3221 else
3223 POINT ptCaretPos;
3224 ptCaretPos.y = nClickedLine;
3225 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3226 SetCaretPosition(ptCaretPos);
3227 ClearSelection();
3229 POINT ptViewCarret = GetCaretViewPosition();
3230 nViewLine = ptViewCarret.y;
3231 if (nViewLine >= GetViewCount())
3232 return;
3233 const CString &sLine = GetViewLine(nViewLine);
3234 int nLineLength = sLine.GetLength();
3235 int nBasePos = ptViewCarret.x;
3236 // get target char group
3237 ECharGroup eLeft = CHG_UNKNOWN;
3238 if (nBasePos > 0)
3240 eLeft = GetCharGroup(sLine[nBasePos-1]);
3242 ECharGroup eRight = CHG_UNKNOWN;
3243 if (nBasePos < nLineLength)
3245 eRight = GetCharGroup(sLine[nBasePos]);
3247 ECharGroup eTarget = max(eRight, eLeft);
3248 // find left margin
3249 int nLeft = nBasePos;
3250 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3252 nLeft--;
3254 // get right margin
3255 int nRight = nBasePos;
3256 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3258 nRight++;
3260 // set selection
3261 m_ptSelectionViewPosStart.x = nLeft;
3262 m_ptSelectionViewPosStart.y = nViewLine;
3263 m_ptSelectionViewPosEnd.x = nRight;
3264 m_ptSelectionViewPosEnd.y = nViewLine;
3265 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3266 SetupAllViewSelection(nViewLine, nViewLine);
3267 // set caret
3268 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3269 UpdateViewsCaretPosition();
3270 UpdateGoalPos();
3272 // set mark word
3273 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3274 int nMarkWidth = max(nRight - nLeft, 0);
3275 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3276 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3278 m_sMarkedWord.Empty();
3281 if (m_pwndLeft)
3282 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3283 if (m_pwndRight)
3284 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3285 if (m_pwndBottom)
3286 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3288 Invalidate();
3289 if (m_pwndLocator)
3290 m_pwndLocator->Invalidate();
3293 CView::OnLButtonDblClk(nFlags, point);
3296 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3298 const int nClickedLine = GetButtonEventLineIndex(point);
3299 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3301 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3303 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3304 if (pidl)
3306 SHOpenFolderAndSelectItems(pidl,0,0,0);
3307 CoTaskMemFree((LPVOID)pidl);
3310 return;
3312 POINT ptCaretPos;
3313 ptCaretPos.y = nClickedLine;
3314 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3315 SetCaretAndGoalPosition(ptCaretPos);
3316 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3317 if (m_pwndLeft)
3318 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3319 if (m_pwndRight)
3320 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3321 if (m_pwndBottom)
3322 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3323 ClearSelection();
3324 m_ptSelectionViewPosStart.x = 0;
3325 m_ptSelectionViewPosStart.y = nClickedLine;
3326 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3327 m_ptSelectionViewPosEnd.y = nClickedLine;
3328 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3329 UpdateViewsCaretPosition();
3330 Invalidate();
3331 if (m_pwndLocator)
3332 m_pwndLocator->Invalidate();
3335 void CBaseView::OnEditCopy()
3337 CString sCopyData = GetSelectedText();
3339 if (!sCopyData.IsEmpty())
3341 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3345 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3347 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3349 --m_pMainFrame->m_nMoveMovesToIgnore;
3350 CView::OnMouseMove(nFlags, point);
3351 return;
3353 int nMouseLine = GetButtonEventLineIndex(point);
3354 if (nMouseLine < -1)
3355 nMouseLine = -1;
3356 m_mouseInMargin = point.x < GetMarginWidth();
3358 ShowDiffLines(nMouseLine);
3360 KillTimer(IDT_SCROLLTIMER);
3361 if (nFlags & MK_LBUTTON)
3363 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3364 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3365 if (saveMouseLine < 0)
3366 return;
3367 int col = CalcColFromPoint(point.x, saveMouseLine);
3368 int charIndex = CalculateCharIndex(saveMouseLine, col);
3369 if (HasSelection() &&
3370 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3372 POINT ptCaretPos = {charIndex, nMouseLine};
3373 SetCaretAndGoalPosition(ptCaretPos);
3374 AdjustSelection(MOVERIGHT);
3375 Invalidate();
3376 UpdateWindow();
3378 if (nMouseLine < m_nTopLine)
3380 ScrollAllToLine(m_nTopLine-1, TRUE);
3381 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3383 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3385 ScrollAllToLine(m_nTopLine+1, TRUE);
3386 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3388 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3390 ScrollAllSide(-1);
3391 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3393 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3395 ScrollAllSide(1);
3396 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3398 SetCapture();
3402 CView::OnMouseMove(nFlags, point);
3405 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3407 ShowDiffLines(-1);
3408 ReleaseCapture();
3409 KillTimer(IDT_SCROLLTIMER);
3411 __super::OnLButtonUp(nFlags, point);
3414 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3416 if (nIDEvent == IDT_SCROLLTIMER)
3418 POINT point;
3419 GetCursorPos(&point);
3420 ScreenToClient(&point);
3421 int nMouseLine = GetButtonEventLineIndex(point);
3422 if (nMouseLine < -1)
3424 nMouseLine = -1;
3426 if (GetKeyState(VK_LBUTTON)&0x8000)
3428 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3429 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3430 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3431 if (nMouseLine < m_nTopLine)
3433 ScrollAllToLine(m_nTopLine-1, TRUE);
3434 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3436 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3438 ScrollAllToLine(m_nTopLine+1, TRUE);
3439 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3441 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3443 ScrollAllSide(-1);
3444 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3446 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3448 ScrollAllSide(1);
3449 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3455 CView::OnTimer(nIDEvent);
3458 void CBaseView::ShowDiffLines(int nLine)
3460 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3462 m_pwndLineDiffBar->ShowLines(nLine);
3463 nLine = -1;
3464 m_nMouseLine = nLine;
3465 return;
3468 if ((!m_pwndRight)||(!m_pwndLeft))
3469 return;
3470 if(m_pMainFrame->m_bOneWay)
3471 return;
3473 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3474 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3476 if (nLine < 0)
3477 return;
3479 if (nLine != m_nMouseLine)
3481 if (nLine >= GetLineCount())
3482 nLine = -1;
3483 m_nMouseLine = nLine;
3484 m_pwndLineDiffBar->ShowLines(nLine);
3486 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3489 const viewdata& CBaseView::GetEmptyLineData()
3491 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3492 return emptyLine;
3495 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3497 for (int i = 0; i < nCount; i++)
3499 InsertViewData(nFirstView, GetEmptyLineData());
3504 void CBaseView::UpdateCaret()
3506 POINT ptCaretPos = GetCaretPosition();
3507 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3508 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3509 SetCaretPosition(ptCaretPos);
3511 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3513 if (m_bFocused &&
3514 ptCaretPos.y >= m_nTopLine &&
3515 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3516 nCaretOffset >= m_nOffsetChar &&
3517 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3519 POINT pt1 = TextToClient(ptCaretPos);
3520 if (m_bInsertMode)
3521 CreateSolidCaret(2, GetLineHeight());
3522 else
3524 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3525 POINT pt2 = TextToClient(pt);
3526 int width = max(GetCharWidth(), pt2.x - pt1.x);
3527 CreateSolidCaret(width, GetLineHeight());
3529 SetCaretPos(pt1);
3530 ShowCaret();
3532 else
3534 HideCaret();
3538 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3540 POINT ptViewPos;
3541 ptViewPos.x = pt.x;
3543 int nSubLine = GetSubLineOffset(pt.y);
3544 if (nSubLine > 0)
3546 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3548 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3552 ptViewPos.y = GetViewLineForScreen(pt.y);
3553 return ptViewPos;
3556 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3558 POINT ptPos;
3559 int nViewLineLenLeft = GetViewLineLength(pt.y);
3560 ptPos.x = min(nViewLineLenLeft, pt.x);
3561 ptPos.y = FindScreenLineForViewLine(pt.y);
3562 if (GetViewLineForScreen(ptPos.y) != pt.y )
3564 ptPos.x = 0;
3566 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3568 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3569 while (nSubLineLength < ptPos.x)
3571 ptPos.x -= nSubLineLength;
3572 nViewLineLenLeft -= nSubLineLength;
3573 ptPos.y++;
3574 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3576 // last pos of non last sub-line go to start of next screen line
3577 // Note: while this works correctly, it's not what a user might expect:
3578 // cursor-right when the caret is before the last char of a wrapped line
3579 // now moves the caret to the next line. But users expect the caret to
3580 // move to the right of the last char instead, and with another cursor-right
3581 // keystroke to move the caret to the next line.
3582 // Basically, this would require to handle two caret positions for the same
3583 // logical position in the line string (one on the last position of the first line,
3584 // one on the first position of the new line. For non-wrapped lines this works
3585 // because there's an 'invisible' newline char at the end of the first line.
3586 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3588 ptPos.x = 0;
3589 ptPos.y++;
3593 return ptPos;
3597 void CBaseView::EnsureCaretVisible()
3599 POINT ptCaretPos = GetCaretPosition();
3600 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3602 if (ptCaretPos.y < m_nTopLine)
3603 ScrollAllToLine(ptCaretPos.y);
3604 int screnLines = GetScreenLines();
3605 if (screnLines)
3607 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3608 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3609 if (nCaretOffset < m_nOffsetChar)
3610 ScrollAllToChar(nCaretOffset);
3611 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3612 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3616 int CBaseView::CalculateActualOffset(const POINT& point)
3618 int nLineIndex = point.y;
3619 int nCharIndex = point.x;
3620 ASSERT(nCharIndex >= 0);
3621 CString sLine = GetLineChars(nLineIndex);
3622 int nLineLength = sLine.GetLength();
3623 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3626 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3628 int nLength = GetLineLength(nLineIndex);
3629 int nSubLine = GetSubLineOffset(nLineIndex);
3630 if (nSubLine>=0)
3632 int nViewLine = GetViewLineForScreen(nLineIndex);
3633 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3635 int nMultilineCount = CountMultiLines(nViewLine);
3636 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3638 nLength--;
3642 CString Line = GetLineChars(nLineIndex);
3643 int nIndex = 0;
3644 int nOffset = 0;
3645 int nTabSize = GetTabSize();
3646 while (nOffset < nActualOffset && nIndex < nLength)
3648 if (Line.GetAt(nIndex) == _T('\t'))
3649 nOffset += (nTabSize - nOffset % nTabSize);
3650 else
3651 ++nOffset;
3652 ++nIndex;
3654 return nIndex;
3658 * @param xpos X coordinate in CBaseView
3659 * @param lineIndex logical line index (e.g. wrap/collapse)
3661 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3663 int xpos2;
3664 CDC *pDC = GetDC();
3665 if (pDC != nullptr)
3667 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3668 int fit = text.GetLength();
3669 std::unique_ptr<int> posBuffer(new int[fit]);
3670 pDC->SelectObject(GetFont()); // is this right font ?
3671 SIZE size;
3672 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3673 ReleaseDC(pDC);
3674 int lower = -1, upper = fit - 1;
3675 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3678 int middle = (upper + lower + 1) / 2;
3679 int width = posBuffer.get()[middle];
3680 if (xcheck < width)
3681 upper = middle - 1;
3682 else
3683 lower = middle;
3684 } while (lower < upper);
3685 lower++;
3686 xpos2 = lower;
3687 if (lower < fit - 1)
3689 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3690 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3691 xpos2++;
3694 else
3696 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3697 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3698 xpos2++;
3700 return xpos2;
3703 POINT CBaseView::TextToClient(const POINT& point)
3705 POINT pt;
3706 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3707 pt.y = nOffsetScreenLine * GetLineHeight();
3708 pt.x = CalculateActualOffset(point);
3710 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3711 CDC * pDC = GetDC();
3712 if (pDC)
3714 pDC->SelectObject(GetFont()); // is this right font ?
3715 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3716 CString sLine = GetLineChars(nScreenLine);
3717 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3718 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3719 ReleaseDC(pDC);
3720 } else {
3721 nLeft += pt.x * GetCharWidth();
3724 pt.x = nLeft;
3725 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3726 return pt;
3729 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3731 CView::OnChar(nChar, nRepCnt, nFlags);
3733 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3734 bool bSkipSelectionClear = false;
3736 if (IsReadonly())
3737 return;
3739 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3740 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3742 return;
3745 if (!m_pViewData) // no data - nothing to do
3746 return;
3748 if (nChar == VK_F16)
3750 // generated by a ctrl+backspace - ignore.
3752 else if (nChar==VK_TAB && HasTextLineSelection())
3754 // change indentation for selected lines
3755 if (bShift)
3757 RemoveIndentationForSelectedBlock();
3759 else
3761 AddIndentationForSelectedBlock();
3763 bSkipSelectionClear = true;
3765 else if ((nChar > 31)||(nChar == VK_TAB))
3767 ResetUndoStep();
3768 RemoveSelectedText();
3769 POINT ptCaretViewPos = GetCaretViewPosition();
3770 int nViewLine = ptCaretViewPos.y;
3771 if ((nViewLine==0)&&(GetViewCount()==0))
3772 OnChar(VK_RETURN, 0, 0);
3773 int charCount = 1;
3774 viewdata lineData = GetViewData(nViewLine);
3775 if (nChar == VK_TAB)
3777 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3778 if (indentChars > 0)
3780 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3781 charCount = indentChars;
3783 else
3784 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3786 else
3788 if (m_bInsertMode)
3789 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3790 else
3792 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3793 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3794 else
3795 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3798 if (IsStateEmpty(lineData.state))
3800 // if not last line set EOL
3801 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3803 if (!IsViewLineEmpty(nCheckViewLine))
3805 lineData.ending = m_lineendings;
3806 break;
3809 // make sure previous (non empty) line have EOL set
3810 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3812 if (!IsViewLineEmpty(nCheckViewLine))
3814 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3816 SetViewLineEnding(nCheckViewLine, m_lineendings);
3818 break;
3822 lineData.state = DIFFSTATE_EDITED;
3823 bool bNeedRenumber = false;
3824 if (lineData.linenumber == -1)
3826 lineData.linenumber = 0;
3827 bNeedRenumber = true;
3829 SetViewData(nViewLine, lineData);
3830 SetModified();
3831 SaveUndoStep();
3832 BuildAllScreen2ViewVector(nViewLine);
3833 if (bNeedRenumber)
3835 UpdateViewLineNumbers();
3837 for (int i = 0; i < charCount; ++i)
3838 MoveCaretRight();
3839 UpdateGoalPos();
3841 else if (nChar == 10)
3843 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3844 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3845 EOL newEOL = EOL_CRLF;
3846 switch (eol)
3848 case EOL_CRLF:
3849 newEOL = EOL_CR;
3850 break;
3851 case EOL_CR:
3852 newEOL = EOL_LF;
3853 break;
3854 case EOL_LF:
3855 newEOL = EOL_CRLF;
3856 break;
3858 if (eol==EOL_NOENDING || eol==newEOL)
3859 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3860 // to add EOL on newly edited empty line hit enter
3861 // don't store into UNDO if no change happened
3862 // and don't mark file as modified
3863 return;
3864 AddUndoViewLine(nViewLine);
3865 m_pViewData->SetLineEnding(nViewLine, newEOL);
3866 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3867 UpdateGoalPos();
3869 else if (nChar == VK_RETURN)
3871 // insert a new, fresh and empty line below the cursor
3872 RemoveSelectedText();
3874 CUndo::GetInstance().BeginGrouping();
3876 POINT ptCaretViewPos = GetCaretViewPosition();
3877 int nViewLine = ptCaretViewPos.y;
3878 int nLeft = ptCaretViewPos.x;
3879 CString sLine = GetViewLineChars(nViewLine);
3880 CString sLineLeft = sLine.Left(nLeft);
3881 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3882 EOL eOriginalEnding = EOL_AUTOLINE;
3883 if (m_pViewData->GetCount() > nViewLine)
3884 eOriginalEnding = GetViewLineEnding(nViewLine);
3886 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3888 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3889 SetViewData(nViewLine, newFirstLine);
3892 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3893 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3894 InsertViewData(nInsertLine, newLastLine);
3895 SetModified();
3896 SaveUndoStep();
3898 // adds new line everywhere except me
3899 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3901 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3903 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3905 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3907 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3909 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3911 SaveUndoStep();
3913 UpdateViewLineNumbers();
3914 SaveUndoStep();
3915 CUndo::GetInstance().EndGrouping();
3917 BuildAllScreen2ViewVector();
3918 // move the cursor to the new line
3919 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3920 SetCaretAndGoalViewPosition(ptCaretViewPos);
3922 else
3923 return; // Unknown control character -- ignore it.
3924 if (!bSkipSelectionClear)
3925 ClearSelection();
3926 EnsureCaretVisible();
3927 UpdateCaret();
3928 Invalidate(FALSE);
3931 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3933 ResetUndoStep();
3934 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3935 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3936 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3937 SetModified();
3938 SaveUndoStep();
3939 RecalcAllVertScrollBars();
3940 Invalidate(FALSE);
3943 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3945 if (m_pViewData == NULL)
3946 return;
3947 int viewLine = nViewLineIndex;
3948 EOL ending = m_pViewData->GetLineEnding(viewLine);
3949 if (ending == EOL_NOENDING)
3951 ending = m_lineendings;
3953 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3954 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3956 CString sPartLine = GetViewLineChars(nViewLineIndex);
3957 int nPosx = GetCaretPosition().x; // should be view pos ?
3958 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3959 sPartLine = sPartLine.Mid(nPosx);
3960 newLine.sLine = sPartLine;
3962 m_pViewData->InsertData(viewLine+1, newLine);
3963 BuildAllScreen2ViewVector();
3966 void CBaseView::RemoveSelectedText()
3968 if (m_pViewData == NULL)
3969 return;
3970 if (!HasTextSelection())
3971 return;
3973 // fix selection if starts or ends on empty line
3974 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3975 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3978 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3979 SetCaretViewPosition(m_ptSelectionViewPosStart);
3980 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3983 m_ptSelectionViewPosStart = GetCaretViewPosition();
3984 if (!HasTextSelection())
3986 ClearSelection();
3987 return;
3990 // We want to undo the insertion in a single step.
3991 ResetUndoStep();
3992 CUndo::GetInstance().BeginGrouping();
3994 // combine first and last line
3995 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3996 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3997 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3998 oFirstLine.ending = oLastLine.ending;
3999 oFirstLine.state = DIFFSTATE_EDITED;
4000 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4002 // clean up middle lines if any
4003 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4005 viewdata oEmptyLine = GetEmptyLineData();
4006 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4008 SetViewData(nViewLine, oEmptyLine);
4010 SaveUndoStep();
4012 if (CleanEmptyLines())
4014 BuildAllScreen2ViewVector(); // schedule full rebuild
4016 SaveUndoStep();
4017 UpdateViewLineNumbers();
4020 SetModified(); //TODO set modified only if real data was changed
4021 SaveUndoStep();
4022 CUndo::GetInstance().EndGrouping();
4024 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4025 SetCaretViewPosition(m_ptSelectionViewPosStart);
4026 UpdateGoalPos();
4027 ClearSelection();
4028 UpdateCaret();
4029 EnsureCaretVisible();
4030 Invalidate(FALSE);
4033 void CBaseView::PasteText()
4035 if (!OpenClipboard())
4036 return;
4038 CString sClipboardText;
4039 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4040 if (hglb)
4042 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4043 sClipboardText = CString(lpstr);
4044 GlobalUnlock(hglb);
4046 hglb = GetClipboardData(CF_UNICODETEXT);
4047 if (hglb)
4049 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4050 sClipboardText = lpstr;
4051 GlobalUnlock(hglb);
4053 CloseClipboard();
4055 if (sClipboardText.IsEmpty())
4056 return;
4058 sClipboardText.Replace(_T("\r\n"), _T("\r"));
4059 sClipboardText.Replace('\n', '\r');
4061 InsertText(sClipboardText);
4064 void CBaseView::OnCaretDown()
4066 POINT ptCaretPos = GetCaretPosition();
4067 int nLine = ptCaretPos.y;
4068 int nNextLine = nLine + 1;
4069 if (nNextLine >= GetLineCount()) // already at last line
4071 return;
4074 POINT ptCaretViewPos = GetCaretViewPosition();
4075 int nViewLine = ptCaretViewPos.y;
4076 int nNextViewLine = GetViewLineForScreen(nNextLine);
4077 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4079 // find next suitable screen line
4080 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4082 nNextLine++;
4083 if (nNextLine >= GetLineCount())
4085 return;
4087 nNextViewLine = GetViewLineForScreen(nNextLine);
4090 ptCaretPos.y = nNextLine;
4091 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4092 SetCaretPosition(ptCaretPos);
4093 OnCaretMove(MOVELEFT);
4094 ShowDiffLines(ptCaretPos.y);
4097 bool CBaseView::MoveCaretLeft()
4099 POINT ptCaretViewPos = GetCaretViewPosition();
4101 //int nViewLine = ptCaretViewPos.y;
4102 if (ptCaretViewPos.x == 0)
4104 int nPrevLine = GetCaretPosition().y;
4105 int nPrevViewLine;
4106 do {
4107 nPrevLine--;
4108 if (nPrevLine < 0)
4110 return false;
4112 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4113 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4114 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4115 ShowDiffLines(nPrevLine);
4117 else
4118 --ptCaretViewPos.x;
4120 SetCaretAndGoalViewPosition(ptCaretViewPos);
4121 return true;
4124 bool CBaseView::MoveCaretRight()
4126 POINT ptCaretViewPos = GetCaretViewPosition();
4128 int nViewLine = ptCaretViewPos.y;
4129 int nViewLineLen = GetViewLineLength(nViewLine);
4130 if (ptCaretViewPos.x >= nViewLineLen)
4132 int nNextLine = GetCaretPosition().y;
4133 int nNextViewLine;
4134 do {
4135 nNextLine++;
4136 if (nNextLine >= GetLineCount())
4138 return false;
4140 nNextViewLine = GetViewLineForScreen(nNextLine);
4141 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4142 ptCaretViewPos.y = nNextViewLine;
4143 ptCaretViewPos.x = 0;
4144 ShowDiffLines(nNextLine);
4146 else
4147 ++ptCaretViewPos.x;
4149 SetCaretAndGoalViewPosition(ptCaretViewPos);
4150 return true;
4153 void CBaseView::UpdateGoalPos()
4155 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4158 void CBaseView::OnCaretLeft()
4160 MoveCaretLeft();
4161 OnCaretMove(MOVELEFT);
4164 void CBaseView::OnCaretRight()
4166 MoveCaretRight();
4167 OnCaretMove(MOVERIGHT);
4170 void CBaseView::OnCaretUp()
4172 POINT ptCaretPos = GetCaretPosition();
4173 int nLine = ptCaretPos.y;
4174 if (nLine <= 0) // already at first line
4176 return;
4178 int nPrevLine = nLine - 1;
4180 POINT ptCaretViewPos = GetCaretViewPosition();
4181 int nViewLine = ptCaretViewPos.y;
4182 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4183 if (nPrevViewLine != nViewLine) // not on same view line
4185 // find previous suitable screen line
4186 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4188 if (nPrevLine <= 0)
4190 return;
4192 nPrevLine--;
4193 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4196 ptCaretPos.y = nPrevLine;
4197 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4198 SetCaretPosition(ptCaretPos);
4199 OnCaretMove(MOVELEFT);
4200 ShowDiffLines(ptCaretPos.y);
4203 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4205 switch (GetCharGroup(ch))
4207 case CHG_CONTROL:
4208 case CHG_WHITESPACE:
4209 case CHG_WORDSEPARATOR:
4210 return true;
4212 return false;
4215 bool CBaseView::IsCaretAtWordBoundary()
4217 POINT ptViewCaret = GetCaretViewPosition();
4218 CString line = GetViewLineChars(ptViewCaret.y);
4219 if (line.IsEmpty())
4220 return false; // no boundary at the empty lines
4221 if (ptViewCaret.x == 0)
4222 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4223 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4224 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4225 return
4226 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4227 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4230 void CBaseView::UpdateViewsCaretPosition()
4232 POINT ptCaretPos = GetCaretPosition();
4233 if (m_pwndBottom && m_pwndBottom!=this)
4234 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4235 if (m_pwndLeft && m_pwndLeft!=this)
4236 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4237 if (m_pwndRight && m_pwndRight!=this)
4238 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4241 void CBaseView::OnCaretWordleft()
4243 MoveCaretWordLeft();
4244 OnCaretMove(MOVELEFT);
4247 void CBaseView::OnCaretWordright()
4249 MoveCaretWordRight();
4250 OnCaretMove(MOVERIGHT);
4253 void CBaseView::MoveCaretWordLeft()
4255 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4260 void CBaseView::MoveCaretWordRight()
4262 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4267 void CBaseView::ClearCurrentSelection()
4269 m_ptSelectionViewPosStart = GetCaretViewPosition();
4270 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4271 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4272 m_nSelViewBlockStart = -1;
4273 m_nSelViewBlockEnd = -1;
4274 Invalidate(FALSE);
4277 void CBaseView::ClearSelection()
4279 if (m_pwndLeft)
4280 m_pwndLeft->ClearCurrentSelection();
4281 if (m_pwndRight)
4282 m_pwndRight->ClearCurrentSelection();
4283 if (m_pwndBottom)
4284 m_pwndBottom->ClearCurrentSelection();
4287 void CBaseView::AdjustSelection(bool bMoveLeft)
4289 POINT ptCaretViewPos = GetCaretViewPosition();
4290 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4292 // select all have been used recently update origin
4293 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4295 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4296 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4298 m_ptSelectionViewPosStart = ptCaretViewPos;
4299 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4301 else
4303 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4304 m_ptSelectionViewPosEnd = ptCaretViewPos;
4307 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4309 Invalidate(FALSE);
4312 void CBaseView::OnEditCut()
4314 if (IsWritable())
4316 OnEditCopy();
4317 RemoveSelectedText();
4321 void CBaseView::OnEditPaste()
4323 if (IsWritable())
4325 CUndo::GetInstance().BeginGrouping();
4326 RemoveSelectedText();
4327 PasteText();
4328 CUndo::GetInstance().EndGrouping();
4332 void CBaseView::DeleteFonts()
4334 for (int i=0; i<fontsCount; i++)
4336 if (m_apFonts[i] != NULL)
4338 m_apFonts[i]->DeleteObject();
4339 delete m_apFonts[i];
4340 m_apFonts[i] = NULL;
4345 void CBaseView::OnCaretMove(bool bMoveLeft)
4347 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4348 OnCaretMove(bMoveLeft, bShift);
4351 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4353 if(isShiftPressed)
4354 AdjustSelection(bMoveLeft);
4355 else
4356 ClearSelection();
4357 EnsureCaretVisible();
4358 UpdateCaret();
4361 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4363 AddCutCopyAndPaste(popup);
4366 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4368 popup.AppendMenu(MF_SEPARATOR, NULL);
4369 CString temp;
4370 temp.LoadString(IDS_EDIT_COPY);
4371 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4372 if (IsWritable())
4374 temp.LoadString(IDS_EDIT_CUT);
4375 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4376 temp.LoadString(IDS_EDIT_PASTE);
4377 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4378 popup.AppendMenu(MF_SEPARATOR, NULL);
4382 void CBaseView::CompensateForKeyboard(CPoint& point)
4384 // if the context menu is invoked through the keyboard, we have to use
4385 // a calculated position on where to anchor the menu on
4386 if (ArePointsSame(point, SetupPoint(-1, -1)))
4388 CRect rect;
4389 GetWindowRect(&rect);
4390 point = rect.CenterPoint();
4394 HICON CBaseView::LoadIcon(WORD iconId)
4396 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4397 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4398 return (HICON)icon;
4401 void CBaseView::ReleaseBitmap()
4403 if (m_pCacheBitmap != NULL)
4405 m_pCacheBitmap->DeleteObject();
4406 delete m_pCacheBitmap;
4407 m_pCacheBitmap = NULL;
4411 void CBaseView::BuildMarkedWordArray()
4413 int lineCount = GetLineCount();
4414 m_arMarkedWordLines.clear();
4415 m_arMarkedWordLines.reserve(lineCount);
4416 bool bDoit = !m_sMarkedWord.IsEmpty();
4417 for (int i = 0; i < lineCount; ++i)
4419 if (bDoit)
4421 CString line = GetLineChars(i);
4423 if (!line.IsEmpty())
4425 int found = 0;
4426 int nMarkStart = -1;
4427 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4429 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4430 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4431 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4432 if (eLeft != eStart)
4434 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4435 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4436 if (eRight != eEnd)
4438 found = 1;
4439 break;
4443 m_arMarkedWordLines.push_back(found);
4445 else
4446 m_arMarkedWordLines.push_back(0);
4448 else
4449 m_arMarkedWordLines.push_back(0);
4453 void CBaseView::BuildFindStringArray()
4455 int lineCount = GetLineCount();
4456 m_arFindStringLines.clear();
4457 m_arFindStringLines.reserve(lineCount);
4458 bool bDoit = !m_sFindText.IsEmpty();
4459 int s = 0;
4460 int e = 0;
4461 for (int i = 0; i < lineCount; ++i)
4463 if (bDoit)
4465 CString line = GetLineChars(i);
4467 if (!line.IsEmpty())
4469 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4471 case DIFFSTATE_EMPTY:
4472 m_arFindStringLines.push_back(0);
4473 break;
4474 case DIFFSTATE_UNKNOWN:
4475 case DIFFSTATE_NORMAL:
4476 if (m_bLimitToDiff)
4478 m_arFindStringLines.push_back(0);
4479 break;
4481 case DIFFSTATE_REMOVED:
4482 case DIFFSTATE_REMOVEDWHITESPACE:
4483 case DIFFSTATE_ADDED:
4484 case DIFFSTATE_ADDEDWHITESPACE:
4485 case DIFFSTATE_WHITESPACE:
4486 case DIFFSTATE_WHITESPACE_DIFF:
4487 case DIFFSTATE_CONFLICTED:
4488 case DIFFSTATE_CONFLICTED_IGNORED:
4489 case DIFFSTATE_CONFLICTADDED:
4490 case DIFFSTATE_CONFLICTEMPTY:
4491 case DIFFSTATE_CONFLICTRESOLVED:
4492 case DIFFSTATE_IDENTICALREMOVED:
4493 case DIFFSTATE_IDENTICALADDED:
4494 case DIFFSTATE_THEIRSREMOVED:
4495 case DIFFSTATE_THEIRSADDED:
4496 case DIFFSTATE_YOURSREMOVED:
4497 case DIFFSTATE_YOURSADDED:
4498 case DIFFSTATE_EDITED:
4500 if (!m_bMatchCase)
4501 line = line.MakeLower();
4502 s = 0;
4503 e = 0;
4504 int match = 0;
4505 while (StringFound(line, SearchNext, s, e))
4507 match++;
4508 s = e;
4509 e = 0;
4511 m_arFindStringLines.push_back(match);
4512 break;
4514 default:
4515 m_arFindStringLines.push_back(0);
4518 else
4519 m_arFindStringLines.push_back(0);
4521 else
4522 m_arFindStringLines.push_back(0);
4524 UpdateLocator();
4527 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4529 if (!m_bShowInlineDiff)
4530 return false;
4531 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4532 return false;
4534 if (m_pViewData == nullptr || m_pViewData->GetCount() <= nViewLine)
4535 return false;
4536 const CString &sLine = m_pViewData->GetLine(nViewLine);
4537 if (sLine.IsEmpty())
4538 return false;
4540 CheckOtherView();
4541 if (!m_pOtherViewData)
4542 return false;
4544 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4545 if (sDiffLine.IsEmpty())
4546 return false;
4548 svn_diff_t * diff = NULL;
4549 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4550 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4551 return false;
4553 size_t lineoffset = 0;
4554 size_t position = 0;
4555 while (diff)
4557 apr_off_t len = diff->original_length;
4558 size_t oldpos = position;
4560 for (apr_off_t i = 0; i < len; ++i)
4562 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4563 lineoffset++;
4566 if (diff->type == svn_diff__type_diff_modified)
4568 inlineDiffPos p;
4569 p.start = oldpos;
4570 p.end = position;
4571 positions.push_back(p);
4574 diff = diff->next;
4577 return !positions.empty();
4580 void CBaseView::OnNavigateNextinlinediff()
4582 int nX;
4583 if (GetNextInlineDiff(nX))
4585 POINT ptCaretViewPos = GetCaretViewPosition();
4586 ptCaretViewPos.x = nX;
4587 SetCaretAndGoalViewPosition(ptCaretViewPos);
4588 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4589 EnsureCaretVisible();
4593 void CBaseView::OnNavigatePrevinlinediff()
4595 int nX;
4596 if (GetPrevInlineDiff(nX))
4598 POINT ptCaretViewPos = GetCaretViewPosition();
4599 ptCaretViewPos.x = nX;
4600 SetCaretAndGoalViewPosition(ptCaretViewPos);
4601 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4602 EnsureCaretVisible();
4606 bool CBaseView::HasNextInlineDiff()
4608 int nPos;
4609 return GetNextInlineDiff(nPos);
4612 bool CBaseView::GetNextInlineDiff(int & nPos)
4614 POINT ptCaretViewPos = GetCaretViewPosition();
4615 std::vector<inlineDiffPos> positions;
4616 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4618 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4620 if (it->start > ptCaretViewPos.x)
4622 nPos = (LONG)it->start;
4623 return true;
4625 if (it->end > ptCaretViewPos.x)
4627 nPos = (LONG)it->end;
4628 return true;
4632 return false;
4635 bool CBaseView::HasPrevInlineDiff()
4637 int nPos;
4638 return GetPrevInlineDiff(nPos);
4641 bool CBaseView::GetPrevInlineDiff(int & nPos)
4643 POINT ptCaretViewPos = GetCaretViewPosition();
4644 std::vector<inlineDiffPos> positions;
4645 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4647 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4649 if ( it->end < ptCaretViewPos.x)
4651 nPos = (LONG)it->end;
4652 return true;
4654 if ( it->start < ptCaretViewPos.x)
4656 nPos = (LONG)it->start;
4657 return true;
4661 return false;
4664 CBaseView * CBaseView::GetFirstGoodView()
4666 if (IsViewGood(m_pwndLeft))
4667 return m_pwndLeft;
4668 if (IsViewGood(m_pwndRight))
4669 return m_pwndRight;
4670 if (IsViewGood(m_pwndBottom))
4671 return m_pwndBottom;
4672 return NULL;
4675 void CBaseView::BuildAllScreen2ViewVector()
4677 CBaseView * p_pwndView = GetFirstGoodView();
4678 if (p_pwndView)
4680 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4684 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4686 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4689 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4691 CBaseView * p_pwndView = GetFirstGoodView();
4692 if (p_pwndView)
4694 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4698 void CBaseView::UpdateViewLineNumbers()
4700 int nLineNumber = 0;
4701 int nViewLineCount = GetViewCount();
4702 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4704 int oldLine = (int)GetViewLineNumber(nViewLine);
4705 if (oldLine >= 0)
4706 SetViewLineNumber(nViewLine, nLineNumber++);
4708 m_nDigits = 0;
4711 int CBaseView::CleanEmptyLines()
4713 int nRemovedCount = 0;
4714 int nViewLineCount = GetViewCount();
4715 bool bCheckLeft = IsViewGood(m_pwndLeft);
4716 bool bCheckRight = IsViewGood(m_pwndRight);
4717 bool bCheckBottom = IsViewGood(m_pwndBottom);
4718 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4720 bool bAllEmpty = true;
4721 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4722 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4723 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4724 if (bAllEmpty)
4726 if (bCheckLeft)
4728 m_pwndLeft->RemoveViewData(nViewLine);
4730 if (bCheckRight)
4732 m_pwndRight->RemoveViewData(nViewLine);
4734 if (bCheckBottom)
4736 m_pwndBottom->RemoveViewData(nViewLine);
4738 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4740 SaveUndoStep();
4742 nViewLineCount--;
4743 nRemovedCount++;
4744 continue;
4746 nViewLine++;
4748 return nRemovedCount;
4751 int CBaseView::FindScreenLineForViewLine( int viewLine )
4753 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4756 int CBaseView::CountMultiLines( int nViewLine )
4758 if (m_ScreenedViewLine.empty())
4759 return 0; // in case the view is completely empty
4761 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4763 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4765 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4768 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4770 TScreenedViewLine oScreenedLine;
4771 // tokenize string
4772 int prevpos = 0;
4773 int pos = 0;
4774 while ((pos = multiline.Find('\n', pos)) >= 0)
4776 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4777 pos++;
4778 prevpos = pos;
4780 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4781 oScreenedLine.bSublinesSet = true;
4782 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4784 return CountMultiLines(nViewLine);
4787 /// prepare inline diff cache
4788 LineColors & CBaseView::GetLineColors(int nViewLine)
4790 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4792 if (m_bWhitespaceInlineDiffs)
4794 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4795 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4797 else
4799 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4800 return m_ScreenedViewLine[nViewLine].lineColors;
4803 LineColors oLineColors;
4804 // set main line color
4805 COLORREF crBkgnd, crText;
4806 DiffStates diffState = m_pViewData->GetState(nViewLine);
4807 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4808 oLineColors.SetColor(0, crText, crBkgnd);
4810 do {
4811 if (!m_bShowInlineDiff)
4812 break;
4814 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4815 break;
4817 CString sLine = GetViewLineChars(nViewLine);
4818 if (sLine.IsEmpty())
4819 break;
4820 if (!m_pOtherView)
4821 break;
4823 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4824 if (sDiffLine.IsEmpty())
4825 break;
4827 svn_diff_t * diff = NULL;
4828 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4829 break;
4830 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4831 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4832 break;
4834 int lineoffset = 0;
4835 int nTextStartOffset = 0;
4836 std::map<int, COLORREF> removedPositions;
4837 while (diff)
4839 apr_off_t len = diff->original_length;
4841 CString s;
4842 for (int i = 0; i < len; ++i)
4844 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4845 lineoffset++;
4847 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4848 int nTextLength = s.GetLength();
4850 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4851 if ((m_bShowInlineDiff)&&(bInlineDiff))
4853 crBkgnd = InlineViewLineDiffColor(nViewLine);
4855 else
4857 crBkgnd = m_ModifiedBk;
4860 if (len < diff->modified_length)
4862 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4864 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4866 nTextStartOffset += nTextLength;
4867 diff = diff->next;
4869 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4871 oLineColors.AddShotColor(it->first, it->second);
4873 } while (false); // error catch
4875 if (!m_bWhitespaceInlineDiffs)
4877 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4878 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4880 else
4882 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4883 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4886 return GetLineColors(nViewLine);
4889 void CBaseView::OnEditSelectall()
4891 if (m_pViewData == nullptr)
4892 return;
4893 int nLastViewLine = m_pViewData->GetCount()-1;
4894 if (nLastViewLine < 0)
4895 return;
4896 SetupAllViewSelection(0, nLastViewLine);
4898 CString sLine = GetViewLineChars(nLastViewLine);
4899 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4900 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4901 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4903 UpdateWindow();
4906 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4908 FilterWhitespaces(first);
4909 FilterWhitespaces(second);
4912 void CBaseView::FilterWhitespaces(CString& line)
4914 line.Remove(' ');
4915 line.Remove('\t');
4916 line.Remove('\r');
4917 line.Remove('\n');
4920 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4922 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4923 int nEventLine = nLineFromTop + m_nTopLine;
4924 nEventLine--; //we need the index
4925 return nEventLine;
4929 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4931 if (RelayTrippleClick(pMsg))
4932 return TRUE;
4933 return CView::PreTranslateMessage(pMsg);
4937 void CBaseView::ResetUndoStep()
4939 m_AllState.Clear();
4942 void CBaseView::SaveUndoStep()
4944 if (!m_AllState.IsEmpty())
4946 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4948 ResetUndoStep();
4951 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4953 m_pState->addedlines.push_back(index);
4954 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4957 void CBaseView::InsertViewData( int index, const viewdata& data )
4959 m_pState->addedlines.push_back(index);
4960 m_pViewData->InsertData(index, data);
4963 void CBaseView::RemoveViewData( int index )
4965 m_pState->removedlines[index] = m_pViewData->GetData(index);
4966 m_pViewData->RemoveData(index);
4969 void CBaseView::SetViewData( int index, const viewdata& data )
4971 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4972 m_pViewData->SetData(index, data);
4975 void CBaseView::SetViewState( int index, DiffStates state )
4977 m_pState->linestates[index] = m_pViewData->GetState(index);
4978 m_pViewData->SetState(index, state);
4981 void CBaseView::SetViewLine( int index, const CString& sLine )
4983 m_pState->difflines[index] = m_pViewData->GetLine(index);
4984 m_pViewData->SetLine(index, sLine);
4987 void CBaseView::SetViewLineNumber( int index, int linenumber )
4989 int oldLineNumber = m_pViewData->GetLineNumber(index);
4990 if (oldLineNumber != linenumber) {
4991 m_pState->linelines[index] = oldLineNumber;
4992 m_pViewData->SetLineNumber(index, linenumber);
4996 void CBaseView::SetViewLineEnding( int index, EOL ending )
4998 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4999 m_pViewData->SetLineEnding(index, ending);
5002 void CBaseView::SetViewMarked( int index, bool marked )
5004 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5005 m_pViewData->SetMarked(index, marked);
5009 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5011 if (HasSelection())
5013 start = m_nSelViewBlockStart;
5014 end = m_nSelViewBlockEnd;
5015 return true;
5017 return false;
5020 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5022 RebuildIfNecessary();
5023 if (size() <= screenLine)
5024 return 0;
5025 return m_Screen2View[screenLine].nViewLine;
5028 int CBaseView::Screen2View::size()
5030 RebuildIfNecessary();
5031 return (int)m_Screen2View.size();
5034 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5036 RebuildIfNecessary();
5037 if (size() <= screenLine)
5038 return 0;
5039 return m_Screen2View[screenLine].nViewSubLine;
5042 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5044 RebuildIfNecessary();
5045 return m_Screen2View[screenLine];
5049 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5051 void CBaseView::Screen2View::RebuildIfNecessary()
5053 if (!m_pViewData)
5054 return; // rebuild not necessary
5056 FixScreenedCacheSize(m_pwndLeft);
5057 FixScreenedCacheSize(m_pwndRight);
5058 FixScreenedCacheSize(m_pwndBottom);
5059 if (!m_bFull)
5061 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5063 ResetScreenedViewLineCache(m_pwndLeft, *it);
5064 ResetScreenedViewLineCache(m_pwndRight, *it);
5065 ResetScreenedViewLineCache(m_pwndBottom, *it);
5068 else
5070 ResetScreenedViewLineCache(m_pwndLeft);
5071 ResetScreenedViewLineCache(m_pwndRight);
5072 ResetScreenedViewLineCache(m_pwndBottom);
5074 m_RebuildRanges.clear();
5075 m_bFull = false;
5077 size_t OldSize = m_Screen2View.size();
5078 m_Screen2View.clear();
5079 m_Screen2View.reserve(OldSize); // guess same size
5080 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5082 if (m_pMainFrame->m_bCollapsed)
5084 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5085 ++i;
5086 if (!(i < m_pViewData->GetCount()))
5087 break;
5089 TScreenLineInfo oLineInfo;
5090 oLineInfo.nViewLine = i;
5091 oLineInfo.nViewSubLine = -1; // no wrap
5092 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5094 int nMaxLines = 0;
5095 if (IsLeftViewGood())
5096 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5097 if (IsRightViewGood())
5098 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5099 if (IsBottomViewGood())
5100 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5101 for (int l = 0; l < (nMaxLines-1); ++l)
5103 oLineInfo.nViewSubLine++;
5104 m_Screen2View.push_back(oLineInfo);
5106 oLineInfo.nViewSubLine++;
5108 m_Screen2View.push_back(oLineInfo);
5110 m_pViewData = NULL;
5112 if (IsLeftViewGood())
5113 m_pwndLeft->BuildMarkedWordArray();
5114 if (IsRightViewGood())
5115 m_pwndRight->BuildMarkedWordArray();
5116 if (IsBottomViewGood())
5117 m_pwndBottom->BuildMarkedWordArray();
5118 UpdateLocator();
5119 RecalcAllVertScrollBars();
5120 RecalcAllHorzScrollBars();
5123 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5125 RebuildIfNecessary();
5127 int nScreenLineCount = (int)m_Screen2View.size();
5129 int nPos = 0;
5130 if (nScreenLineCount>16)
5132 // for enough long data search for last screen
5133 // with viewline less than one we are looking for
5134 // use approximate method (based on) binary search using asymmetric start point
5135 // in form 2**n (determined as MSB of length) to go around division and rounding;
5136 // this effectively looks for bit values from MSB to LSB
5138 int nTestBit;
5139 //GetMostSignificantBitValue
5140 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5141 nTestBit = nScreenLineCount;
5142 nTestBit |= nTestBit>>1;
5143 nTestBit |= nTestBit>>2;
5144 nTestBit |= nTestBit>>4;
5145 nTestBit |= nTestBit>>8;
5146 nTestBit |= nTestBit>>16;
5147 nTestBit ^= (nTestBit>>1);
5149 while (nTestBit)
5151 int nTestPos = nPos | nTestBit;
5152 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5154 nPos = nTestPos;
5156 nTestBit >>= 1;
5159 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5161 nPos++;
5164 return nPos;
5167 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5168 m_bFull = true;
5170 m_pViewData = pViewData;
5173 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5175 if (m_bFull)
5176 return;
5178 m_pViewData = pViewData;
5180 TRebuildRange Range;
5181 Range.FirstViewLine=nFirstViewLine;
5182 Range.LastViewLine=nLastViewLine;
5183 m_RebuildRanges.push_back(Range);
5186 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5188 if (!IsViewGood(pwndView))
5190 return false;
5192 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5193 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5194 if (nOldSize == nViewCount)
5196 return false;
5198 pwndView->m_ScreenedViewLine.resize(nViewCount);
5199 return true;
5202 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5204 if (!IsViewGood(pwndView))
5206 return false;
5208 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5209 ResetScreenedViewLineCache(pwndView, Range);
5210 return true;
5213 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5215 if (!IsViewGood(pwndView))
5217 return false;
5219 if (Range.LastViewLine == -1)
5221 return false;
5223 ASSERT(Range.FirstViewLine >= 0);
5224 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5225 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5227 pwndView->m_ScreenedViewLine[i].Clear();
5229 return false;
5232 void CBaseView::WrapChanged()
5234 m_nMaxLineLength = -1;
5235 m_nOffsetChar = 0;
5238 void CBaseView::OnEditFind()
5240 if (m_pFindDialog)
5241 return;
5243 m_pFindDialog = new CFindDlg(this);
5244 m_pFindDialog->Create(this);
5246 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5247 m_pFindDialog->SetReadonly(m_bReadonly);
5250 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5252 ASSERT(m_pFindDialog != NULL);
5254 if (m_pFindDialog->IsTerminating())
5256 // invalidate the handle identifying the dialog box.
5257 m_pFindDialog = NULL;
5258 return 0;
5261 if(m_pFindDialog->FindNext())
5263 //read data from dialog
5264 m_sFindText = m_pFindDialog->GetFindString();
5265 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5266 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5267 m_bWholeWord = m_pFindDialog->WholeWord();
5269 if (!m_bMatchCase)
5270 m_sFindText = m_sFindText.MakeLower();
5272 BuildFindStringArray();
5273 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5275 if (m_pFindDialog->SearchUp())
5276 OnEditFindprev();
5277 else
5278 OnEditFindnext();
5280 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5282 size_t count = 0;
5283 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5284 count += m_arFindStringLines[i];
5285 CString format;
5286 format.LoadString(IDS_FIND_COUNT);
5287 CString matches;
5288 matches.Format(format, count);
5289 m_pFindDialog->SetStatusText(matches);
5291 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5293 if (!IsWritable())
5294 return 0;
5295 bool bFound = false;
5296 if (m_pFindDialog->SearchUp())
5297 bFound = Search(SearchPrevious, true, true, false);
5298 else
5299 bFound = Search(SearchNext, true, true, false);
5300 if (bFound)
5302 CString sReplaceText = m_pFindDialog->GetReplaceString();
5303 CUndo::GetInstance().BeginGrouping();
5304 RemoveSelectedText();
5305 InsertText(sReplaceText);
5306 CUndo::GetInstance().EndGrouping();
5310 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5312 if (!IsWritable())
5313 return 0;
5314 bool bFound = false;
5315 int replaceCount = 0;
5316 POINT lastPoint = m_ptSelectionViewPosStart;
5317 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5318 CUndo::GetInstance().BeginGrouping();
5321 bFound = Search(SearchNext, true, false, true);
5322 if (bFound)
5324 CString sReplaceText = m_pFindDialog->GetReplaceString();
5325 RemoveSelectedText();
5326 InsertText(sReplaceText);
5327 ++replaceCount;
5329 } while (bFound);
5330 CUndo::GetInstance().EndGrouping();
5331 if (replaceCount == 0)
5332 m_ptSelectionViewPosStart = lastPoint;
5333 CString message;
5334 message.Format(IDS_FIND_REPLACED, replaceCount);
5335 if (m_pFindDialog)
5336 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5341 return 0;
5344 void CBaseView::OnEditFindnextStart()
5346 if (m_pViewData == nullptr)
5347 return;
5348 if (HasTextSelection())
5350 m_sFindText = GetSelectedText();
5351 m_bMatchCase = false;
5352 m_bLimitToDiff = false;
5353 m_bWholeWord = false;
5354 m_sFindText = m_sFindText.MakeLower();
5356 BuildFindStringArray();
5357 OnEditFindnext();
5359 else
5361 m_sFindText.Empty();
5362 BuildFindStringArray();
5366 void CBaseView::OnEditFindprevStart()
5368 if (m_pViewData == nullptr)
5369 return;
5370 if (HasTextSelection())
5372 m_sFindText = GetSelectedText();
5373 m_bMatchCase = false;
5374 m_bLimitToDiff = false;
5375 m_bWholeWord = false;
5376 m_sFindText = m_sFindText.MakeLower();
5378 BuildFindStringArray();
5379 OnEditFindprev();
5381 else
5383 m_sFindText.Empty();
5384 BuildFindStringArray();
5388 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5390 if (srchDir == SearchPrevious)
5392 int laststart = -1;
5393 int laststart2 = -1;
5396 laststart2 = laststart;
5397 laststart = str.Find(m_sFindText, laststart + 1);
5398 } while (laststart >= 0 && laststart < start);
5399 start = laststart2;
5401 else
5402 start = str.Find(m_sFindText, start);
5403 end = start + m_sFindText.GetLength();
5404 bool bStringFound = (start >= 0);
5405 if (bStringFound && m_bWholeWord)
5407 if (start)
5408 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5410 if (bStringFound)
5412 if (str.GetLength() > end)
5413 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5416 return bStringFound;
5419 void CBaseView::OnEditFindprev()
5421 Search(SearchPrevious, false, true, false);
5424 void CBaseView::OnEditFindnext()
5426 Search(SearchNext, false, true, false);
5429 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5431 if (m_sFindText.IsEmpty())
5432 return false;
5433 if(!m_pViewData)
5434 return false;
5436 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5437 POINT end;
5438 end.y = m_pViewData->GetCount()-1;
5439 if (end.y < 0)
5440 return false;
5442 if (srchDir==SearchNext)
5443 end.x = GetViewLineLength(end.y);
5444 else
5446 end.x = m_ptSelectionViewPosStart.x;
5447 start.x = 0;
5450 if (!HasTextSelection())
5452 start.y = m_ptCaretViewPos.y;
5453 if (srchDir==SearchNext)
5454 start.x = m_ptCaretViewPos.x;
5455 else
5457 start.x = 0;
5458 end.x = m_ptCaretViewPos.x;
5461 CString sSelectedText;
5462 int startline = -1;
5463 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5465 if (nViewLine < 0)
5467 if (stopEof)
5468 return false;
5469 nViewLine = m_pViewData->GetCount()-1;
5470 startline = start.y;
5471 if (flashIfNotFound)
5473 if (m_pFindDialog)
5474 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5475 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5478 if (nViewLine > end.y)
5480 if (stopEof)
5481 return false;
5482 nViewLine = 0;
5483 startline = start.y;
5484 if (flashIfNotFound)
5486 if (m_pFindDialog)
5487 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5488 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5491 switch (m_pViewData->GetState(nViewLine))
5493 case DIFFSTATE_EMPTY:
5494 break;
5495 case DIFFSTATE_UNKNOWN:
5496 case DIFFSTATE_NORMAL:
5497 if (m_bLimitToDiff)
5498 break;
5499 case DIFFSTATE_REMOVED:
5500 case DIFFSTATE_REMOVEDWHITESPACE:
5501 case DIFFSTATE_ADDED:
5502 case DIFFSTATE_ADDEDWHITESPACE:
5503 case DIFFSTATE_WHITESPACE:
5504 case DIFFSTATE_WHITESPACE_DIFF:
5505 case DIFFSTATE_CONFLICTED:
5506 case DIFFSTATE_CONFLICTED_IGNORED:
5507 case DIFFSTATE_CONFLICTADDED:
5508 case DIFFSTATE_CONFLICTEMPTY:
5509 case DIFFSTATE_CONFLICTRESOLVED:
5510 case DIFFSTATE_IDENTICALREMOVED:
5511 case DIFFSTATE_IDENTICALADDED:
5512 case DIFFSTATE_THEIRSREMOVED:
5513 case DIFFSTATE_THEIRSADDED:
5514 case DIFFSTATE_YOURSREMOVED:
5515 case DIFFSTATE_YOURSADDED:
5516 case DIFFSTATE_EDITED:
5518 sSelectedText = GetViewLineChars(nViewLine);
5519 if (nViewLine == start.y && startline < 0)
5520 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5521 if (!m_bMatchCase)
5522 sSelectedText = sSelectedText.MakeLower();
5523 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5524 int endfound = 0;
5525 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5527 HighlightViewLines(nViewLine, nViewLine);
5528 m_ptSelectionViewPosStart.x = startfound;
5529 m_ptSelectionViewPosEnd.x = endfound;
5530 if (nViewLine == start.y && startline < 0)
5532 m_ptSelectionViewPosStart.x += start.x;
5533 m_ptSelectionViewPosEnd.x += start.x;
5535 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5536 m_ptSelectionViewPosStart.y = nViewLine;
5537 m_ptSelectionViewPosEnd.y = nViewLine;
5538 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5539 UpdateViewsCaretPosition();
5540 EnsureCaretVisible();
5541 Invalidate();
5542 return true;
5545 break;
5548 if (startline >= 0)
5550 if (nViewLine == startline)
5552 if (flashIfNotFound)
5554 CString message;
5555 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5556 if (m_pFindDialog)
5557 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5558 ::MessageBeep(0xFFFFFFFF);
5559 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5561 break;
5565 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5566 return false;
5569 CString CBaseView::GetSelectedText() const
5571 CString sSelectedText;
5572 POINT start = m_ptSelectionViewPosStart;
5573 POINT end = m_ptSelectionViewPosEnd;
5574 if (!HasTextSelection())
5576 if (!HasSelection())
5577 return sSelectedText;
5578 start.y = m_nSelViewBlockStart;
5579 start.x = 0;
5580 end.y = m_nSelViewBlockEnd;
5581 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5583 if (m_pViewData == nullptr)
5584 return sSelectedText;
5585 // first store the selected lines in one CString
5586 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5588 switch (m_pViewData->GetState(nViewLine))
5590 case DIFFSTATE_EMPTY:
5591 break;
5592 case DIFFSTATE_UNKNOWN:
5593 case DIFFSTATE_NORMAL:
5594 case DIFFSTATE_REMOVED:
5595 case DIFFSTATE_REMOVEDWHITESPACE:
5596 case DIFFSTATE_ADDED:
5597 case DIFFSTATE_ADDEDWHITESPACE:
5598 case DIFFSTATE_WHITESPACE:
5599 case DIFFSTATE_WHITESPACE_DIFF:
5600 case DIFFSTATE_CONFLICTED:
5601 case DIFFSTATE_CONFLICTED_IGNORED:
5602 case DIFFSTATE_CONFLICTADDED:
5603 case DIFFSTATE_CONFLICTEMPTY:
5604 case DIFFSTATE_CONFLICTRESOLVED:
5605 case DIFFSTATE_IDENTICALREMOVED:
5606 case DIFFSTATE_IDENTICALADDED:
5607 case DIFFSTATE_THEIRSREMOVED:
5608 case DIFFSTATE_THEIRSADDED:
5609 case DIFFSTATE_YOURSREMOVED:
5610 case DIFFSTATE_YOURSADDED:
5611 case DIFFSTATE_EDITED:
5612 sSelectedText += GetViewLineChars(nViewLine);
5613 sSelectedText += _T("\r\n");
5614 break;
5617 // remove the non-selected chars from the first line, last line and last \r\n
5618 int nLeftCut = start.x;
5619 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5620 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5621 return sSelectedText;
5624 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5626 hasMods = false;
5627 hasConflicts = false;
5628 hasWhitespaceMods = false;
5630 if (m_pViewData)
5632 for (int i=0; i<m_pViewData->GetCount(); i++)
5634 DiffStates state = m_pViewData->GetState(i);
5635 switch (state)
5637 case DIFFSTATE_ADDED:
5638 case DIFFSTATE_IDENTICALADDED:
5639 case DIFFSTATE_THEIRSADDED:
5640 case DIFFSTATE_YOURSADDED:
5641 case DIFFSTATE_CONFLICTADDED:
5642 case DIFFSTATE_IDENTICALREMOVED:
5643 case DIFFSTATE_REMOVED:
5644 case DIFFSTATE_THEIRSREMOVED:
5645 case DIFFSTATE_YOURSREMOVED:
5646 case DIFFSTATE_EMPTY:
5647 hasMods = true;
5648 break;
5649 case DIFFSTATE_CONFLICTED:
5650 case DIFFSTATE_CONFLICTED_IGNORED:
5651 hasConflicts = true;
5652 break;
5653 case DIFFSTATE_REMOVEDWHITESPACE:
5654 case DIFFSTATE_ADDEDWHITESPACE:
5655 case DIFFSTATE_WHITESPACE:
5656 case DIFFSTATE_WHITESPACE_DIFF:
5657 hasWhitespaceMods = true;
5658 break;
5664 void CBaseView::OnEditGotoline()
5666 if (m_pViewData == NULL)
5667 return;
5668 // find the last and first line number
5669 int nViewLineCount = m_pViewData->GetCount();
5671 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5672 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5674 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5675 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5677 break;
5680 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5682 return;
5684 nLastLineNumber++;
5685 int nFirstLineNumber=1; // first is always 1
5687 CString sText;
5688 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5690 CGotoLineDlg dlg(this);
5691 dlg.SetLabel(sText);
5692 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5693 if (dlg.DoModal() == IDOK)
5695 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5697 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5699 HighlightViewLines(nViewLine, nViewLine);
5700 return;
5706 void CBaseView::OnToggleReadonly()
5708 if (IsReadonlyChangable()) {
5709 SetWritable(IsReadonly());
5713 int CBaseView::SaveFile(int nFlags)
5715 Invalidate();
5716 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5718 CFileTextLines file;
5719 m_SaveParams.m_LineEndings = m_lineendings;
5720 m_SaveParams.m_UnicodeType = m_texttype;
5721 file.SetSaveParams(m_SaveParams);
5723 for (int i=0; i<m_pViewData->GetCount(); i++)
5725 //only copy non-removed lines
5726 DiffStates state = m_pViewData->GetState(i);
5727 switch (state)
5729 case DIFFSTATE_CONFLICTED:
5730 case DIFFSTATE_CONFLICTED_IGNORED:
5732 int first = i;
5733 int last = i;
5736 last++;
5737 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5738 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5739 for (int j=first; j<last; j++)
5741 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5743 file.Add(_T("======="), EOL_NOENDING);
5744 for (int j=first; j<last; j++)
5746 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5748 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5749 i = last-1;
5751 break;
5752 case DIFFSTATE_EMPTY:
5753 break;
5754 case DIFFSTATE_CONFLICTEMPTY:
5755 case DIFFSTATE_IDENTICALREMOVED:
5756 case DIFFSTATE_REMOVED:
5757 case DIFFSTATE_THEIRSREMOVED:
5758 case DIFFSTATE_YOURSREMOVED:
5759 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5760 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5762 // do not save removed lines
5763 break;
5765 default:
5766 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5767 break;
5770 CString filename = m_pWorkingFile->GetFilename();
5771 if (m_pWorkingFile->IsReadonly())
5772 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5773 return -1;
5774 if (!file.Save(filename))
5776 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5777 return -1;
5779 m_pWorkingFile->SetFileName(filename);
5780 m_pWorkingFile->StoreFileAttributes();
5781 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5782 SetModified(FALSE);
5783 CUndo::GetInstance().MarkAsOriginalState(
5784 this == m_pwndLeft,
5785 this == m_pwndRight,
5786 this == m_pwndBottom);
5787 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5788 return 0;
5789 return file.GetCount();
5791 return 1;
5795 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5797 if (m_pWorkingFile)
5799 m_pWorkingFile->SetFileName(sFileName);
5800 return SaveFile(nFlags);
5802 return -1;
5806 EOL CBaseView::GetLineEndings()
5808 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5811 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5813 if (bHasMixedEols)
5815 return EOL_AUTOLINE; // mixed eols - hack value
5817 if (m_lineendings == EOL_AUTOLINE)
5819 return EOL_CRLF;
5821 return m_lineendings;
5824 void CBaseView::ReplaceLineEndings(EOL eEol)
5826 if (eEol == EOL_AUTOLINE)
5828 return;
5830 // set AUTOLINE
5831 m_lineendings = eEol;
5832 // replace all set EOLs
5833 // TODO store line endings and lineendings in undo
5834 //CUndo::BeginGrouping();
5835 for (int i = 0; i < GetViewCount(); ++i)
5837 if (IsLineEmpty(i))
5839 continue;
5841 EOL eLineEol = GetViewLineEnding(i);
5842 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5844 continue;
5846 SetViewLineEnding(i, eEol);
5848 //CUndo::EndGrouping();
5849 //CUndo::saveundostep;
5850 DocumentUpdated();
5851 SetModified();
5854 void CBaseView::SetLineEndingStyle(EOL eEol)
5856 m_lineendings = eEol;
5859 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5861 if (m_texttype == eTextType)
5863 return;
5865 m_texttype = eTextType;
5866 DocumentUpdated();
5867 SetModified();
5870 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5872 if (IsReadonly())
5873 return; // nothing to be changed in read-only view
5874 CEncodingDlg dlg;
5875 dlg.view = CString(MAKEINTRESOURCE(nTextId));
5876 dlg.texttype = m_texttype;
5877 dlg.lineendings = GetLineEndings();
5878 if (dlg.DoModal() != IDOK)
5879 return;
5880 SetTextType(dlg.texttype);
5881 ReplaceLineEndings(dlg.lineendings);
5885 Replaces lines from source view to this
5887 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
5889 if (!IsViewGood(pwndView))
5890 return;
5891 if (!IsWritable())
5892 return;
5893 CUndo::GetInstance().BeginGrouping();
5895 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5897 bool skip = fnSkip(viewLine);
5898 if (skip)
5900 if (GetViewMarked(viewLine))
5901 SetViewMarked(viewLine, false);
5902 continue;
5904 viewdata line = pwndView->GetViewData(viewLine);
5905 if (line.ending != EOL_NOENDING)
5906 line.ending = m_lineendings;
5907 switch (line.state)
5909 case DIFFSTATE_CONFLICTEMPTY:
5910 case DIFFSTATE_UNKNOWN:
5911 line.state = DIFFSTATE_EMPTY;
5912 case DIFFSTATE_EMPTY:
5913 break;
5914 case DIFFSTATE_ADDED:
5915 case DIFFSTATE_CONFLICTADDED:
5916 case DIFFSTATE_CONFLICTED:
5917 case DIFFSTATE_CONFLICTED_IGNORED:
5918 case DIFFSTATE_IDENTICALADDED:
5919 case DIFFSTATE_THEIRSADDED:
5920 case DIFFSTATE_YOURSADDED:
5921 case DIFFSTATE_IDENTICALREMOVED:
5922 case DIFFSTATE_REMOVED:
5923 case DIFFSTATE_THEIRSREMOVED:
5924 case DIFFSTATE_YOURSREMOVED:
5925 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5926 line.state = DIFFSTATE_NORMAL;
5927 case DIFFSTATE_NORMAL:
5928 break;
5929 default:
5930 break;
5932 bool marked = GetViewMarked(viewLine);
5933 SetViewData(viewLine, line);
5934 if (marked)
5935 SetViewMarked(viewLine, false);
5936 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5938 // if this view is in ASCII and the other is not, we have to make sure that
5939 // the text we copy from the other view can actually be saved in ASCII encoding.
5940 // if not, we have to change this views encoding to the same encoding as the other view
5941 BOOL useDefault = FALSE;
5942 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5943 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5944 SetTextType(pwndView->GetTextType());
5947 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5948 // TODO: check if copied line is same as original one set modified only when differ
5949 SetModified();
5950 SaveUndoStep();
5952 int nRemovedLines = CleanEmptyLines();
5953 SaveUndoStep();
5954 //VerifyEols();
5955 // make sure all non empty line have EOL set but last
5956 // wrong can be last copied line(have eol, but no line under),
5957 // or old last line (line before copied block missing eol, but have line under)
5958 // we'll check all lines to be sure
5959 int nLine = GetViewCount();
5960 // check last line have no EOL set
5961 while (--nLine>=0)
5963 if (!IsViewLineEmpty(nLine))
5965 if (GetViewLineEnding(nLine) != EOL_NOENDING)
5967 // we added non last line into empty block on the end (or should we remove eol from this one ?)
5968 // so next line is empty
5969 ASSERT(IsViewLineEmpty(nLine+1));
5970 // and we can turn it to normal empty line
5971 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
5973 break;
5976 // check all (nonlast) line have EOL set
5977 while (--nLine>=0)
5979 if (!IsViewLineEmpty(nLine))
5981 if (GetViewLineEnding(nLine) == EOL_NOENDING)
5983 SetViewLineEnding(nLine, m_lineendings);
5984 // in theory there should be only one line needing fix, but most of time we get over all anyway
5985 // break;
5989 SaveUndoStep();
5990 UpdateViewLineNumbers();
5991 SaveUndoStep();
5993 CUndo::GetInstance().EndGrouping();
5995 if (nRemovedLines!=0)
5997 // some lines are gone update selection
5998 ClearSelection();
5999 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6001 BuildAllScreen2ViewVector();
6002 pwndView->Invalidate();
6003 RefreshViews();
6006 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6008 if (!IsWritable())
6009 return;
6010 CUndo::GetInstance().BeginGrouping();
6012 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6013 SetViewMarked(viewLine, marked);
6015 SetModified();
6016 SaveUndoStep();
6017 CUndo::GetInstance().EndGrouping();
6019 BuildAllScreen2ViewVector();
6020 Invalidate();
6021 RefreshViews();
6024 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6026 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6027 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6030 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6032 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6033 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6036 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6038 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6039 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6042 int CBaseView::GetIndentCharsForLine(int x, int y)
6044 const int maxGuessLine = 100;
6045 int nTabMode = -1;
6046 CString line = GetViewLine(y);
6047 if (m_nTabMode & TABMODE_SMARTINDENT)
6049 // detect left char and right char
6050 TCHAR lc = x > 0 ? line[x - 1] : '\0';
6051 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
6052 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
6053 nTabMode = 1;
6054 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
6055 nTabMode = 0;
6056 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
6057 nTabMode = m_nTabMode & TABMODE_USESPACES;
6059 // detect lines nearby
6060 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6062 bool above = i > 0 && i >= y - maxGuessLine;
6063 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6064 if (!(above || below))
6065 break;
6066 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
6067 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
6068 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
6069 nTabMode = 1;
6070 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
6071 nTabMode = 0;
6072 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
6073 nTabMode = m_nTabMode & TABMODE_USESPACES;
6076 else
6077 nTabMode = m_nTabMode & TABMODE_USESPACES;
6079 if (nTabMode > 0)
6081 // use spaces
6082 x = CountExpandedChars(line, x);
6083 return (m_nTabSize - (x % m_nTabSize));
6086 // use tab
6087 return 0;
6090 void CBaseView::AddIndentationForSelectedBlock()
6092 bool bModified = false;
6093 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6095 // skip the line if no character is selected in the last selected line
6096 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6098 continue;
6100 // skip empty lines
6101 if (IsLineEmpty(nViewLine))
6103 continue;
6105 const CString &sLine = GetViewLine(nViewLine);
6106 CString sTemp = sLine;
6107 if (sTemp.Trim().IsEmpty())
6109 // skip empty and whitechar only lines
6110 continue;
6112 // add tab to line start (alternatively m_nTabSize spaces can be used)
6113 CString tabStr;
6114 int indentChars = GetIndentCharsForLine(0, nViewLine);
6115 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
6116 SetViewLine(nViewLine, tabStr + sLine);
6117 bModified = true;
6119 if (bModified)
6121 SetModified();
6122 SaveUndoStep();
6123 BuildAllScreen2ViewVector();
6127 void CBaseView::RemoveIndentationForSelectedBlock()
6129 bool bModified = false;
6130 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6132 // skip the line if no character is selected in the last selected line
6133 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6135 continue;
6137 // skip empty lines
6138 if (IsLineEmpty(nViewLine))
6140 continue;
6142 CString sLine = GetViewLine(nViewLine);
6143 // remove up to n spaces from line start
6144 // and one tab (if less then n spaces was removed)
6145 int nPos = 0;
6146 while (nPos<m_nTabSize)
6148 switch (sLine[nPos])
6150 case ' ':
6151 nPos++;
6152 continue;
6153 case '\t':
6154 nPos++;
6156 break;
6158 if (nPos>0)
6160 sLine.Delete(0, nPos);
6161 SetViewLine(nViewLine, sLine);
6162 bModified = true;
6165 if (bModified)
6167 SetModified();
6168 SaveUndoStep();
6169 BuildAllScreen2ViewVector();
6174 there are two possible versions
6175 - convert tabs to spaces only in front of text (implemented)
6176 - convert all tabs to spaces
6178 void CBaseView::ConvertTabToSpaces()
6180 bool bModified = false;
6181 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6183 if (IsLineEmpty(nViewLine))
6185 continue;
6187 const CString &sLine = GetViewLine(nViewLine);
6188 bool bTabToConvertFound = false;
6189 int nPosIn = 0;
6190 int nPosOut = 0;
6191 while (nPosIn<sLine.GetLength())
6193 switch (sLine[nPosIn])
6195 case ' ':
6196 nPosIn++;
6197 nPosOut++;
6198 continue;
6199 case '\t':
6200 nPosIn++;
6201 bTabToConvertFound = true;
6202 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6203 continue;
6205 break;
6207 if (bTabToConvertFound)
6209 CString sLineNew = sLine;
6210 sLineNew.Delete(0, nPosIn);
6211 sLineNew = CString(' ', nPosOut) + sLineNew;
6212 SetViewLine(nViewLine, sLineNew);
6213 bModified = true;
6216 if (bModified)
6218 SetModified();
6219 SaveUndoStep();
6220 BuildAllScreen2ViewVector();
6225 there are two possible version
6226 - convert spaces to tabs only in front of text (implemented)
6227 - convert all spaces to tabs
6229 void CBaseView::Tabularize()
6231 bool bModified = false;
6232 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6234 if (IsLineEmpty(nViewLine))
6236 continue;
6238 const CString &sLine = GetViewLine(nViewLine);
6239 int nDel = 0;
6240 int nTabCount = 0; // total tabs to be used
6241 int nSpaceCount = 0; // number of spaces in tab size run
6242 int nPos = 0;
6243 while (nPos<sLine.GetLength())
6245 switch (sLine[nPos++])
6247 case ' ':
6248 //bSpace = true;
6249 if (++nSpaceCount < m_nTabSize)
6251 continue;
6253 case '\t':
6254 nTabCount++;
6255 nSpaceCount = 0;
6256 nDel = nPos;
6257 continue;
6259 break;
6261 if (nDel > 0)
6263 CString sLineNew = sLine;
6264 sLineNew.Delete(0, nDel);
6265 sLineNew = CString('\t', nTabCount) + sLineNew;
6266 if (sLine!=sLineNew)
6268 SetViewLine(nViewLine, sLineNew);
6269 bModified = true;
6273 if (bModified)
6275 SetModified();
6276 SaveUndoStep();
6277 BuildAllScreen2ViewVector();
6281 void CBaseView::RemoveTrailWhiteChars()
6283 bool bModified = false;
6284 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6286 if (IsLineEmpty(nViewLine))
6288 continue;
6290 const CString &sLine = GetViewLine(nViewLine);
6291 CString sLineNew = sLine;
6292 sLineNew.TrimRight();
6293 if (sLine.GetLength()!=sLineNew.GetLength())
6295 SetViewLine(nViewLine, sLineNew);
6296 bModified = true;
6299 if (bModified)
6301 SetModified();
6302 SaveUndoStep();
6303 BuildAllScreen2ViewVector();
6307 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6309 if (GetViewCount()>10000)
6311 // 10k lines is enough to check
6312 TWhitecharsProperties oRet = {true, true, true, true};
6313 return oRet;
6315 TWhitecharsProperties oRet = {};
6316 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6318 if (IsLineEmpty(nViewLine))
6320 continue;
6322 const CString &sLine = GetViewLine(nViewLine);
6323 if (sLine.IsEmpty())
6325 continue;
6327 // check leading whites for convertible tabs and spaces
6328 int nPos = 0;
6329 int nSpaceCount = 0; // number of spaces in tab size run
6330 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6332 switch (sLine[nPos++])
6334 case ' ':
6335 if (++nSpaceCount >= m_nTabSize)
6337 oRet.HasSpacesToConvert = true;
6339 continue;
6340 case '\t':
6341 oRet.HasTabsToConvert = true;
6342 if (nSpaceCount!=0)
6344 oRet.HasSpacesToConvert = true;
6346 continue;
6348 break;
6351 // check trailing whites for removable chars
6352 switch (sLine[sLine.GetLength()-1])
6354 case ' ':
6355 case '\t':
6356 oRet.HasTrailWhiteChars = true;
6359 // check EOLs
6360 EOL eLineEol = GetViewLineEnding(nViewLine);
6361 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6363 oRet.HasMixedEols = true;
6366 return oRet;
6369 void CBaseView::InsertText(const CString& sText)
6371 ResetUndoStep();
6373 POINT ptCaretViewPos = GetCaretViewPosition();
6374 int nLeft = ptCaretViewPos.x;
6375 int nViewLine = ptCaretViewPos.y;
6377 if ((nViewLine == 0) && (GetViewCount() == 0))
6378 OnChar(VK_RETURN, 0, 0);
6380 std::vector<CString> lines;
6381 int nStart = 0;
6382 int nEolPos = 0;
6383 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6385 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6386 lines.push_back(sLine);
6387 nEolPos++;
6388 nStart = nEolPos;
6390 CString sLine = sText.Mid(nStart);
6391 lines.push_back(sLine);
6393 int nLinesToPaste = (int)lines.size();
6394 if (nLinesToPaste > 1)
6396 // multiline text
6398 // We want to undo the multiline insertion in a single step.
6399 CUndo::GetInstance().BeginGrouping();
6401 sLine = GetViewLineChars(nViewLine);
6402 CString sLineLeft = sLine.Left(nLeft);
6403 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6404 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6405 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6406 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6408 newLine.sLine = sLineLeft + lines[0];
6409 SetViewData(nViewLine, newLine);
6412 int nInsertLine = nViewLine;
6413 for (int i = 1; i < nLinesToPaste - 1; i++)
6415 newLine.sLine = lines[i];
6416 InsertViewData(++nInsertLine, newLine);
6418 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6419 newLine.ending = eOriginalEnding;
6420 InsertViewData(++nInsertLine, newLine);
6422 SetModified();
6423 SaveUndoStep();
6425 // adds new lines everywhere except me
6426 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6428 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6430 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6432 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6434 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6436 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6438 SaveUndoStep();
6440 UpdateViewLineNumbers();
6441 CUndo::GetInstance().EndGrouping();
6443 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6445 else
6447 // single line text - just insert it
6448 sLine = GetViewLineChars(nViewLine);
6449 sLine.Insert(nLeft, sText);
6450 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6451 SetViewLine(nViewLine, sLine);
6452 SetViewState(nViewLine, DIFFSTATE_EDITED);
6453 SetModified();
6454 SaveUndoStep();
6457 RefreshViews();
6458 BuildAllScreen2ViewVector();
6459 UpdateCaretViewPosition(ptCaretViewPos);