Don't cache the number of lines on the screen if the scroll bar is not visible
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blobb660987c3b18d970f69e441e42ce54e0cd2f69e0
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2015 - 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")), _countof(m_lfBaseFont.lfFaceName) - 1);
504 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
506 delete m_apFonts[nIndex];
507 m_apFonts[nIndex] = NULL;
508 return CView::GetFont();
511 return m_apFonts[nIndex];
514 void CBaseView::CalcLineCharDim()
516 CDC *pDC = GetDC();
517 if (pDC == nullptr)
518 return;
519 CFont *pOldFont = pDC->SelectObject(GetFont());
520 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
521 pDC->SelectObject(pOldFont);
522 ReleaseDC(pDC);
524 m_nLineHeight = szCharExt.cy;
525 if (m_nLineHeight <= 0)
526 m_nLineHeight = -1;
527 m_nCharWidth = szCharExt.cx;
528 if (m_nCharWidth <= 0)
529 m_nCharWidth = -1;
532 int CBaseView::GetScreenChars()
534 if (m_nScreenChars == -1)
536 CRect rect;
537 GetClientRect(&rect);
538 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
539 if (m_nScreenChars < 0)
540 m_nScreenChars = 0;
542 return m_nScreenChars;
545 int CBaseView::GetAllMinScreenChars() const
547 int nChars = INT_MAX;
548 if (IsLeftViewGood())
549 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
550 if (IsRightViewGood())
551 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
552 if (IsBottomViewGood())
553 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
554 return (nChars==INT_MAX) ? 0 : nChars;
557 int CBaseView::GetAllMaxLineLength() const
559 int nLength = 0;
560 if (IsLeftViewGood())
561 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
562 if (IsRightViewGood())
563 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
564 if (IsBottomViewGood())
565 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
566 return nLength;
569 int CBaseView::GetLineHeight()
571 if (m_nLineHeight == -1)
572 CalcLineCharDim();
573 if (m_nLineHeight <= 0)
574 return 1;
575 return m_nLineHeight;
578 int CBaseView::GetCharWidth()
580 if (m_nCharWidth == -1)
581 CalcLineCharDim();
582 if (m_nCharWidth <= 0)
583 return 1;
584 return m_nCharWidth;
587 int CBaseView::GetMaxLineLength()
589 if (m_nMaxLineLength == -1)
591 m_nMaxLineLength = 0;
592 int nLineCount = GetLineCount();
593 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 CRect rect;
834 GetClientRect(&rect);
835 int scrollBarHeight = 0;
836 SCROLLBARINFO sbi = { sizeof(sbi) };
837 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
839 // only use the scroll bar size if the info is correct and the scrollbar is visible
840 // if anything isn't proper, assume the scrollbar has a size of zero
841 // and calculate the screen lines without it.
842 if (!(sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) && !(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
844 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
845 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
846 if (m_nScreenLines < 0)
847 m_nScreenLines = 0;
848 return m_nScreenLines;
851 // if the scroll bar is not visible, unavailable or there was an error,
852 // assume the scroll bar height is zero and return the screen lines here.
853 // Of course, that means the cache (using the member variable) won't work
854 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
855 return (rect.Height() - HEADERHEIGHT) / GetLineHeight();
857 return m_nScreenLines;
860 int CBaseView::GetAllMinScreenLines() const
862 int nLines = INT_MAX;
863 if (IsLeftViewGood())
864 nLines = m_pwndLeft->GetScreenLines();
865 if (IsRightViewGood())
866 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
867 if (IsBottomViewGood())
868 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
869 return (nLines == INT_MAX) || (nLines < 0) ? 0 : nLines;
872 int CBaseView::GetAllLineCount() const
874 int nLines = 0;
875 if (IsLeftViewGood())
876 nLines = m_pwndLeft->GetLineCount();
877 if (IsRightViewGood())
878 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
879 if (IsBottomViewGood())
880 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
881 return nLines;
884 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
886 if (IsLeftViewGood())
887 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
888 if (IsRightViewGood())
889 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
890 if (IsBottomViewGood())
891 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
894 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
896 SCROLLINFO si;
897 si.cbSize = sizeof(si);
898 if (bPositionOnly)
900 si.fMask = SIF_POS;
901 si.nPos = m_nTopLine;
903 else
905 EnableScrollBarCtrl(SB_VERT, TRUE);
906 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
908 m_nTopLine = 0;
909 Invalidate();
911 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
912 si.nMin = 0;
913 si.nMax = GetAllLineCount();
914 si.nPage = GetAllMinScreenLines();
915 si.nPos = m_nTopLine;
917 VERIFY(SetScrollInfo(SB_VERT, &si));
920 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
922 CView::OnVScroll(nSBCode, nPos, pScrollBar);
923 if (m_pwndLeft)
924 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
925 if (m_pwndRight)
926 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
927 if (m_pwndBottom)
928 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
929 if (m_pwndLocator)
930 m_pwndLocator->Invalidate();
933 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
935 // Note we cannot use nPos because of its 16-bit nature
936 SCROLLINFO si;
937 si.cbSize = sizeof(si);
938 si.fMask = SIF_ALL;
939 VERIFY(master->GetScrollInfo(SB_VERT, &si));
941 int nPageLines = GetScreenLines();
942 int nLineCount = GetLineCount();
944 int nNewTopLine;
946 static LONG textwidth = 0;
947 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
948 switch (nSBCode)
950 case SB_TOP:
951 nNewTopLine = 0;
952 break;
953 case SB_BOTTOM:
954 nNewTopLine = nLineCount - nPageLines + 1;
955 break;
956 case SB_LINEUP:
957 nNewTopLine = m_nTopLine - 1;
958 break;
959 case SB_LINEDOWN:
960 nNewTopLine = m_nTopLine + 1;
961 break;
962 case SB_PAGEUP:
963 nNewTopLine = m_nTopLine - si.nPage + 1;
964 break;
965 case SB_PAGEDOWN:
966 nNewTopLine = m_nTopLine + si.nPage - 1;
967 break;
968 case SB_THUMBPOSITION:
969 m_ScrollTool.Clear();
970 nNewTopLine = si.nTrackPos;
971 textwidth = 0;
972 break;
973 case SB_THUMBTRACK:
974 nNewTopLine = si.nTrackPos;
975 if (GetFocus() == this)
977 RECT thumbrect;
978 GetClientRect(&thumbrect);
979 ClientToScreen(&thumbrect);
981 POINT thumbpoint;
982 thumbpoint.x = thumbrect.right;
983 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
984 m_ScrollTool.Init(&thumbpoint);
985 if (textwidth == 0)
987 CString sTemp = sFormat;
988 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
989 textwidth = m_ScrollTool.GetTextWidth(sTemp);
991 thumbpoint.x -= textwidth;
992 int line = GetLineNumber(nNewTopLine);
993 if (line >= 0)
994 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
995 else
996 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
998 break;
999 default:
1000 return;
1003 if (nNewTopLine < 0)
1004 nNewTopLine = 0;
1005 if (nNewTopLine >= nLineCount)
1006 nNewTopLine = nLineCount - 1;
1007 ScrollToLine(nNewTopLine);
1010 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
1012 if (IsLeftViewGood())
1013 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
1014 if (IsRightViewGood())
1015 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
1016 if (IsBottomViewGood())
1017 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
1020 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
1022 SCROLLINFO si;
1023 si.cbSize = sizeof(si);
1024 if (bPositionOnly)
1026 si.fMask = SIF_POS;
1027 si.nPos = m_nOffsetChar;
1029 else
1031 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
1032 if (!m_pMainFrame->m_bWrapLines)
1034 int minScreenChars = GetAllMinScreenChars();
1035 int maxLineLength = GetAllMaxLineLength();
1036 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1038 m_nOffsetChar = 0;
1039 Invalidate();
1041 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1042 si.nMin = 0;
1043 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1044 si.nMax += GetMarginWidth()/GetCharWidth();
1045 si.nPage = GetScreenChars();
1046 si.nPos = m_nOffsetChar;
1049 VERIFY(SetScrollInfo(SB_HORZ, &si));
1052 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1054 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1055 if (m_pwndLeft)
1056 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1057 if (m_pwndRight)
1058 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1059 if (m_pwndBottom)
1060 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1061 if (m_pwndLocator)
1062 m_pwndLocator->Invalidate();
1065 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1067 SCROLLINFO si;
1068 si.cbSize = sizeof(si);
1069 si.fMask = SIF_ALL;
1070 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1072 int nPageChars = GetScreenChars();
1073 int nMaxLineLength = GetMaxLineLength();
1075 int nNewOffset;
1076 switch (nSBCode)
1078 case SB_LEFT:
1079 nNewOffset = 0;
1080 break;
1081 case SB_BOTTOM:
1082 nNewOffset = nMaxLineLength - nPageChars + 1;
1083 break;
1084 case SB_LINEUP:
1085 nNewOffset = m_nOffsetChar - 1;
1086 break;
1087 case SB_LINEDOWN:
1088 nNewOffset = m_nOffsetChar + 1;
1089 break;
1090 case SB_PAGEUP:
1091 nNewOffset = m_nOffsetChar - si.nPage + 1;
1092 break;
1093 case SB_PAGEDOWN:
1094 nNewOffset = m_nOffsetChar + si.nPage - 1;
1095 break;
1096 case SB_THUMBPOSITION:
1097 case SB_THUMBTRACK:
1098 nNewOffset = si.nTrackPos;
1099 break;
1100 default:
1101 return;
1104 if (nNewOffset >= nMaxLineLength)
1105 nNewOffset = nMaxLineLength - 1;
1106 if (nNewOffset < 0)
1107 nNewOffset = 0;
1108 ScrollToChar(nNewOffset, TRUE);
1111 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1113 if (m_nOffsetChar != nNewOffsetChar)
1115 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1116 m_nOffsetChar = nNewOffsetChar;
1117 CRect rcScroll;
1118 GetClientRect(&rcScroll);
1119 rcScroll.left += GetMarginWidth();
1120 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1121 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1122 // update the view header
1123 rcScroll.left = 0;
1124 rcScroll.top = 0;
1125 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1126 InvalidateRect(&rcScroll, FALSE);
1127 UpdateWindow();
1128 if (bTrackScrollBar)
1129 RecalcHorzScrollBar(TRUE);
1130 UpdateCaret();
1131 if (m_pwndLineDiffBar)
1132 m_pwndLineDiffBar->Invalidate();
1136 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1138 if (m_pwndLeft)
1139 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1140 if (m_pwndRight)
1141 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1142 if (m_pwndBottom)
1143 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1146 void CBaseView::ScrollAllSide(int delta)
1148 int nNewOffset = m_nOffsetChar;
1149 nNewOffset += delta;
1150 int nMaxLineLength = GetMaxLineLength();
1151 if (nNewOffset >= nMaxLineLength)
1152 nNewOffset = nMaxLineLength - 1;
1153 if (nNewOffset < 0)
1154 nNewOffset = 0;
1155 ScrollAllToChar(nNewOffset, TRUE);
1156 if (m_pwndLineDiffBar)
1157 m_pwndLineDiffBar->Invalidate();
1158 UpdateCaret();
1161 void CBaseView::ScrollSide(int delta)
1163 int nNewOffset = m_nOffsetChar;
1164 nNewOffset += delta;
1165 int nMaxLineLength = GetMaxLineLength();
1166 if (nNewOffset >= nMaxLineLength)
1167 nNewOffset = nMaxLineLength - 1;
1168 if (nNewOffset < 0)
1169 nNewOffset = 0;
1170 ScrollToChar(nNewOffset, TRUE);
1171 if (m_pwndLineDiffBar)
1172 m_pwndLineDiffBar->Invalidate();
1173 UpdateCaret();
1176 void CBaseView::ScrollVertical(short zDelta)
1178 const int nLineCount = GetLineCount();
1179 int nTopLine = m_nTopLine;
1180 nTopLine -= (zDelta/30);
1181 if (nTopLine < 0)
1182 nTopLine = 0;
1183 if (nTopLine >= nLineCount)
1184 nTopLine = nLineCount - 1;
1185 ScrollToLine(nTopLine, TRUE);
1188 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1190 if (m_nTopLine != nNewTopLine)
1192 if (nNewTopLine < 0)
1193 nNewTopLine = 0;
1195 int nScrollLines = m_nTopLine - nNewTopLine;
1197 m_nTopLine = nNewTopLine;
1198 CRect rcScroll;
1199 GetClientRect(&rcScroll);
1200 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1201 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1202 UpdateWindow();
1203 if (bTrackScrollBar)
1204 RecalcVertScrollBar(TRUE);
1205 UpdateCaret();
1210 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1212 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1214 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1216 int nViewLine = GetViewLineForScreen(nLineIndex);
1217 HICON icon = NULL;
1218 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1219 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1220 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1222 DiffStates state = m_pViewData->GetState(nViewLine);
1223 switch (state)
1225 case DIFFSTATE_ADDED:
1226 case DIFFSTATE_THEIRSADDED:
1227 case DIFFSTATE_YOURSADDED:
1228 case DIFFSTATE_IDENTICALADDED:
1229 case DIFFSTATE_CONFLICTADDED:
1230 eIcon = TScreenedViewLine::ICN_ADD;
1231 break;
1232 case DIFFSTATE_REMOVED:
1233 case DIFFSTATE_THEIRSREMOVED:
1234 case DIFFSTATE_YOURSREMOVED:
1235 case DIFFSTATE_IDENTICALREMOVED:
1236 eIcon = TScreenedViewLine::ICN_REMOVED;
1237 break;
1238 case DIFFSTATE_CONFLICTED:
1239 eIcon = TScreenedViewLine::ICN_CONFLICT;
1240 break;
1241 case DIFFSTATE_CONFLICTED_IGNORED:
1242 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1243 break;
1244 case DIFFSTATE_EDITED:
1245 eIcon = TScreenedViewLine::ICN_EDIT;
1246 break;
1247 default:
1248 break;
1250 bool bIdentical = false;
1251 int blockstart = -1;
1252 int blockend = -1;
1253 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1255 if (bIdentical)
1256 eIcon = TScreenedViewLine::ICN_SAME;
1257 else
1258 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1259 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1261 if (nViewLine > blockstart)
1262 Invalidate(); // redraw the upper icons since they're now changing
1263 while (blockstart <= blockend)
1264 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1267 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1268 eIcon = TScreenedViewLine::ICN_MOVED;
1269 if (m_pViewData->GetMarked(nViewLine))
1270 eIcon = TScreenedViewLine::ICN_MARKED;
1271 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1273 switch (eIcon)
1275 case TScreenedViewLine::ICN_UNKNOWN:
1276 case TScreenedViewLine::ICN_NONE:
1277 break;
1278 case TScreenedViewLine::ICN_SAME:
1279 icon = m_hEqualIcon;
1280 break;
1281 case TScreenedViewLine::ICN_EDIT:
1282 icon = m_hEditedIcon;
1283 break;
1284 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1285 icon = m_hWhitespaceBlockIcon;
1286 break;
1287 case TScreenedViewLine::ICN_ADD:
1288 icon = m_hAddedIcon;
1289 break;
1290 case TScreenedViewLine::ICN_CONFLICT:
1291 icon = m_hConflictedIcon;
1292 break;
1293 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1294 icon = m_hConflictedIgnoredIcon;
1295 break;
1296 case TScreenedViewLine::ICN_REMOVED:
1297 icon = m_hRemovedIcon;
1298 break;
1299 case TScreenedViewLine::ICN_MOVED:
1300 icon = m_hMovedIcon;
1301 break;
1302 case TScreenedViewLine::ICN_MARKED:
1303 icon = m_hMarkedIcon;
1304 break;
1308 if (icon)
1310 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1312 if ((m_bViewLinenumbers)&&(m_nDigits))
1314 int nSubLine = GetSubLineOffset(nLineIndex);
1315 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1316 CString sLinenumber;
1317 if (bIsFirstSubline)
1319 CString sLinenumberFormat;
1320 int nLineNumber = GetLineNumber(nLineIndex);
1321 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1323 // TODO: do not show if there is no number hidden
1324 // TODO: show number if there is only one
1325 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1326 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1328 else if (nLineNumber >= 0)
1330 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1331 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1333 else if (m_pMainFrame->m_bWrapLines)
1335 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1336 sLinenumber.Format(sLinenumberFormat, _T("·"));
1338 if (!sLinenumber.IsEmpty())
1340 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1341 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1343 pdc->SelectObject(GetFont());
1344 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1351 int CBaseView::GetMarginWidth()
1353 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1355 if (m_nDigits <= 0)
1357 int nLength = (int)m_pViewData->GetCount();
1358 // find out how many digits are needed to show the highest line number
1359 CString sMax;
1360 sMax.Format(_T("%d"), nLength);
1361 m_nDigits = sMax.GetLength();
1363 int nWidth = GetCharWidth();
1364 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1366 return MARGINWIDTH;
1369 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1371 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1372 COLORREF crBk, crFg;
1373 if (IsBottomViewGood())
1375 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1376 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1378 else
1380 DiffStates state = DIFFSTATE_REMOVED;
1381 if (this == m_pwndRight)
1383 state = DIFFSTATE_ADDED;
1385 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1387 pdc->SetBkColor(crBk);
1388 pdc->FillSolidRect(textrect, crBk);
1390 pdc->SetTextColor(crFg);
1392 pdc->SelectObject(GetFont(FALSE, TRUE));
1394 CString sViewTitle;
1395 if (IsModified())
1397 sViewTitle = _T("* ") + m_sWindowName;
1399 else
1401 sViewTitle = m_sWindowName;
1403 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1404 if (nStringLength > rect.Width())
1406 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1407 sViewTitle = m_sWindowName.Mid(offset);
1409 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1410 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1411 if (this->GetFocus() == this)
1412 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1413 else
1414 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1417 void CBaseView::OnDraw(CDC * pDC)
1419 CRect rcClient;
1420 GetClientRect(rcClient);
1422 int nLineCount = GetLineCount();
1423 int nLineHeight = GetLineHeight();
1425 CDC cacheDC;
1426 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1427 if (m_pCacheBitmap == NULL)
1429 m_pCacheBitmap = new CBitmap;
1430 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1432 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1434 DrawHeader(pDC, rcClient);
1436 CRect rcLine;
1437 rcLine = rcClient;
1438 rcLine.top += nLineHeight+HEADERHEIGHT;
1439 rcLine.bottom = rcLine.top + nLineHeight;
1440 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1441 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1443 int nCurrentLine = m_nTopLine;
1444 bool bBeyondFileLineCached = false;
1445 while (rcLine.top < rcClient.bottom)
1447 if (nCurrentLine < nLineCount)
1449 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1450 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1451 bBeyondFileLineCached = false;
1453 else if (!bBeyondFileLineCached)
1455 DrawMargin(&cacheDC, rcCacheMargin, -1);
1456 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1457 bBeyondFileLineCached = true;
1460 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1462 nCurrentLine ++;
1463 rcLine.OffsetRect(0, nLineHeight);
1466 cacheDC.SelectObject(pOldBitmap);
1467 cacheDC.DeleteDC();
1470 bool CBaseView::IsStateConflicted(DiffStates state)
1472 switch (state)
1474 case DIFFSTATE_CONFLICTED:
1475 case DIFFSTATE_CONFLICTED_IGNORED:
1476 case DIFFSTATE_CONFLICTEMPTY:
1477 case DIFFSTATE_CONFLICTADDED:
1478 return true;
1480 return false;
1483 bool CBaseView::IsStateEmpty(DiffStates state)
1485 switch (state)
1487 case DIFFSTATE_CONFLICTEMPTY:
1488 case DIFFSTATE_UNKNOWN:
1489 case DIFFSTATE_EMPTY:
1490 return true;
1492 return false;
1495 bool CBaseView::IsStateRemoved(DiffStates state)
1497 switch (state)
1499 case DIFFSTATE_REMOVED:
1500 case DIFFSTATE_THEIRSREMOVED:
1501 case DIFFSTATE_YOURSREMOVED:
1502 case DIFFSTATE_IDENTICALREMOVED:
1503 return true;
1505 return false;
1508 DiffStates CBaseView::ResolveState(DiffStates state)
1510 if (IsStateConflicted(state))
1512 if (state == DIFFSTATE_CONFLICTEMPTY)
1513 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1514 else
1515 return DIFFSTATE_CONFLICTRESOLVED;
1517 return state;
1521 bool CBaseView::IsLineEmpty(int nLineIndex)
1523 if (m_pViewData == 0)
1524 return FALSE;
1525 int nViewLine = GetViewLineForScreen(nLineIndex);
1526 return IsViewLineEmpty(nViewLine);
1529 bool CBaseView::IsViewLineEmpty(int nViewLine)
1531 if (m_pViewData == 0)
1532 return FALSE;
1533 const DiffStates state = m_pViewData->GetState(nViewLine);
1534 return IsStateEmpty(state);
1537 bool CBaseView::IsLineRemoved(int nLineIndex)
1539 if (m_pViewData == 0)
1540 return FALSE;
1541 int nViewLine = GetViewLineForScreen(nLineIndex);
1542 return IsViewLineRemoved(nViewLine);
1545 bool CBaseView::IsViewLineRemoved(int nViewLine)
1547 if (m_pViewData == 0)
1548 return FALSE;
1549 const DiffStates state = m_pViewData->GetState(nViewLine);
1550 return IsStateRemoved(state);
1553 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1555 if (m_pViewData == 0)
1556 return false;
1557 const DiffStates state = m_pViewData->GetState(nLineIndex);
1558 return IsStateConflicted(state);
1561 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1563 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1566 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1568 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1571 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1573 if (origin.x < (rc.left - GetCharWidth() +1))
1574 return;
1575 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1576 return;
1577 int viewLine = GetViewLineForScreen(nLineIndex);
1578 EOL ending = m_pViewData->GetLineEnding(viewLine);
1579 if (m_bIconLFs)
1581 HICON hEndingIcon = NULL;
1582 switch (ending)
1584 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1585 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1586 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1587 default: return;
1589 // If EOL style has changed, color end-of-line markers as inline differences.
1591 m_bShowInlineDiff && m_pOtherViewData &&
1592 (viewLine < m_pOtherViewData->GetCount()) &&
1593 (ending != EOL_NOENDING) &&
1594 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1595 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1598 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1601 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1603 else
1605 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1606 CPen * oldpen = pDC->SelectObject(&pen);
1607 int yMiddle = origin.y + rc.Height()/2;
1608 int xMiddle = origin.x+GetCharWidth()/2;
1609 bool bMultiline = false;
1610 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1612 if (GetLineLength(nLineIndex+1))
1614 // multiline
1615 bMultiline = true;
1616 pDC->MoveTo(origin.x, yMiddle-2);
1617 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1618 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1619 pDC->LineTo(origin.x, yMiddle+2);
1621 else if (GetLineLength(nLineIndex) == 0)
1622 bMultiline = true;
1624 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1625 bMultiline = true;
1627 if (!bMultiline)
1629 switch (ending)
1631 case EOL_AUTOLINE:
1632 case EOL_CRLF:
1633 // arrow from top to middle+2, then left
1634 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1635 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1636 case EOL_CR:
1637 // arrow from right to left
1638 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1639 pDC->LineTo(origin.x, yMiddle);
1640 pDC->LineTo(origin.x+4, yMiddle+4);
1641 pDC->MoveTo(origin.x, yMiddle);
1642 pDC->LineTo(origin.x+4, yMiddle-4);
1643 break;
1644 case EOL_LFCR:
1645 // from right-upper to left then down
1646 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1647 pDC->LineTo(xMiddle, yMiddle-2);
1648 pDC->LineTo(xMiddle, rc.bottom-1);
1649 pDC->LineTo(xMiddle+4, rc.bottom-5);
1650 pDC->MoveTo(xMiddle, rc.bottom-1);
1651 pDC->LineTo(xMiddle-4, rc.bottom-5);
1652 break;
1653 case EOL_LF:
1654 // arrow from top to bottom
1655 pDC->MoveTo(xMiddle, rc.top);
1656 pDC->LineTo(xMiddle, rc.bottom-1);
1657 pDC->LineTo(xMiddle+4, rc.bottom-5);
1658 pDC->MoveTo(xMiddle, rc.bottom-1);
1659 pDC->LineTo(xMiddle-4, rc.bottom-5);
1660 break;
1661 case EOL_FF: // Form Feed, U+000C
1662 case EOL_NEL: // Next Line, U+0085
1663 case EOL_LS: // Line Separator, U+2028
1664 case EOL_PS: // Paragraph Separator, U+2029
1665 // draw a horizontal line at the bottom of this line
1666 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1667 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1668 pDC->LineTo(origin.x, rc.bottom-2);
1669 pDC->LineTo(origin.x+5, rc.bottom-2);
1670 pDC->MoveTo(origin.x, rc.bottom-2);
1671 pDC->LineTo(origin.x+1, rc.bottom-6);
1672 break;
1673 default: // other EOLs
1674 // arrow from top right to bottom left
1675 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1676 pDC->LineTo(origin.x, rc.bottom-1);
1677 pDC->LineTo(origin.x+5, rc.bottom-2);
1678 pDC->MoveTo(origin.x, rc.bottom-1);
1679 pDC->LineTo(origin.x+1, rc.bottom-6);
1680 break;
1681 case EOL_NOENDING:
1682 break;
1685 pDC->SelectObject(oldpen);
1689 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1691 if (!m_bShowSelection)
1692 return;
1694 int nSelBlockStart;
1695 int nSelBlockEnd;
1696 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1697 return;
1699 const int THICKNESS = 2;
1700 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1702 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1703 int nSubLine = GetSubLineOffset(nLineIndex);
1704 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1705 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1707 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1710 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1711 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1713 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1717 void CBaseView::DrawTextLine(
1718 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1720 ASSERT(nLineIndex < GetLineCount());
1721 int nViewLine = GetViewLineForScreen(nLineIndex);
1722 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1724 LineColors lineCols = GetLineColors(nViewLine);
1726 CString sViewLine = GetViewLineChars(nViewLine);
1727 // mark selection
1728 if (m_bShowSelection && HasTextSelection())
1730 // has this line selection ?
1731 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1733 int nViewLineLength = sViewLine.GetLength();
1735 // first suppose the whole line is selected
1736 int selectedStart = 0;
1737 int selectedEnd = nViewLineLength;
1739 // the view line is partially selected
1740 if (m_ptSelectionViewPosStart.y == nViewLine)
1742 selectedStart = m_ptSelectionViewPosStart.x;
1745 if (m_ptSelectionViewPosEnd.y == nViewLine)
1747 selectedEnd = m_ptSelectionViewPosEnd.x;
1749 // apply selection coloring
1750 // First enforce start and end point
1751 lineCols.SplitBlock(selectedStart);
1752 lineCols.SplitBlock(selectedEnd);
1753 // change color of affected parts
1754 long intenseColorScale = m_bFocused ? 70 : 30;
1755 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1756 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1758 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1759 if (it->second.shot == it->second.background)
1761 it->second.shot = crBk;
1763 it->second.background = crBk;
1764 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1769 // TODO: remove duplicate from selection and mark
1770 if (!m_sMarkedWord.IsEmpty())
1772 int nMarkLength = m_sMarkedWord.GetLength();
1773 //int nViewLineLength = sViewLine.GetLength();
1774 const TCHAR * text = sViewLine;
1775 const TCHAR * findText = text;
1776 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1778 int nMarkStart = static_cast<int>(findText - text);
1779 int nMarkEnd = nMarkStart + nMarkLength;
1780 findText += nMarkLength;
1781 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1782 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1783 if (eLeft == eStart)
1784 continue;
1785 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1786 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1787 if (eRight == eEnd)
1788 continue;
1790 // First enforce start and end point
1791 lineCols.SplitBlock(nMarkStart);
1792 lineCols.SplitBlock(nMarkEnd);
1793 // change color of affected parts
1794 const long int nIntenseColorScale = 200;
1795 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1796 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1798 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1799 if (it->second.shot == it->second.background)
1801 it->second.shot = crBk;
1803 it->second.background = crBk;
1804 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1808 if (!m_sFindText.IsEmpty())
1810 int nMarkStart = 0;
1811 int nMarkEnd = 0;
1812 int nStringPos = nMarkStart;
1813 CString searchLine = sViewLine;
1814 if (!m_bMatchCase)
1815 searchLine.MakeLower();
1816 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1818 // First enforce start and end point
1819 lineCols.SplitBlock(nMarkStart+nStringPos);
1820 lineCols.SplitBlock(nMarkEnd+nStringPos);
1821 // change color of affected parts
1822 const long int nIntenseColorScale = 30;
1823 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1824 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1826 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1827 if (it->second.shot == it->second.background)
1829 it->second.shot = crBk;
1831 it->second.background = crBk;
1832 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1834 searchLine = searchLine.Mid(nMarkEnd);
1835 nStringPos = nMarkEnd;
1836 nMarkStart = 0;
1837 nMarkEnd = 0;
1841 // @ this point we may cache data for next line which may be same in wrapped mode
1843 int nTextOffset = 0;
1844 int nSubline = GetSubLineOffset(nLineIndex);
1845 for (int n=0; n<nSubline; n++)
1847 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1848 nTextOffset += sLine.GetLength();
1851 CString sLine = GetLineChars(nLineIndex);
1852 int nLineLength = sLine.GetLength();
1853 CString sLineExp = ExpandChars(sLine);
1854 LPCTSTR textExp = sLineExp;
1855 //int nLineLengthExp = sLineExp.GetLength();
1856 int nStartExp = 0;
1857 int nLeft = coords.x;
1858 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1860 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1861 ++itEnd;
1862 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1863 int nEnd = nLineLength;
1864 if (itEnd != lineCols.end())
1866 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1868 int nBlockLength = nEnd - nStart;
1869 if (nBlockLength > 0 && nEnd>=0)
1871 pDC->SetBkColor(itStart->second.background);
1872 pDC->SetTextColor(itStart->second.text);
1873 int nEndExp = CountExpandedChars(sLine, nEnd);
1874 int nTextLength = nEndExp - nStartExp;
1875 LPCTSTR p_zBlockText = textExp + nStartExp;
1876 SIZE Size;
1877 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1878 int nRight = nLeft + Size.cx;
1879 if ((nRight > rc.left) && (nLeft < rc.right))
1881 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1882 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1883 // is 4094 (4095 doesn't work anymore).
1884 // So we limit the length here to that 4094 chars.
1885 // In case we're scrolled to the right, there's no need to draw the string
1886 // from way outside our window, so we also offset the drawing to the start of the window.
1887 // This reduces the string length as well.
1888 int offset = 0;
1889 int leftcoord = nLeft;
1890 if (nLeft < 0)
1892 int fit = nTextLength;
1893 std::unique_ptr<int> posBuffer(new int[fit]);
1894 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1895 int lower = 0, upper = fit - 1;
1898 int middle = (upper + lower + 1) / 2;
1899 int width = posBuffer.get()[middle];
1900 if (rc.left - nLeft < width)
1901 upper = middle - 1;
1902 else
1903 lower = middle;
1904 } while (lower < upper);
1906 offset = lower;
1907 nTextLength -= offset;
1908 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1911 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1912 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1914 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1917 nLeft = nRight;
1918 coords.x = nRight;
1919 nStartExp = nEndExp;
1924 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1926 if (nLineIndex >= GetLineCount())
1927 nLineIndex = -1;
1928 ASSERT(nLineIndex >= -1);
1930 if ((nLineIndex == -1) || !m_pViewData)
1932 // Draw line beyond the text
1933 COLORREF crBkgnd, crText;
1934 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1935 pDC->FillSolidRect(rc, crBkgnd);
1936 return;
1939 int viewLine = GetViewLineForScreen(nLineIndex);
1940 if (m_pMainFrame->m_bCollapsed)
1942 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1944 COLORREF crBkgnd, crText;
1945 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1946 pDC->FillSolidRect(rc, crBkgnd);
1948 const int THICKNESS = 2;
1949 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1950 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1951 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1952 pDC->SetBkColor(crBkgnd);
1953 CRect rect = rc;
1954 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1955 return;
1959 DiffStates diffState = m_pViewData->GetState(viewLine);
1960 COLORREF crBkgnd, crText;
1961 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1963 if (diffState == DIFFSTATE_CONFLICTED)
1965 // conflicted lines are shown without 'text' on them
1966 CRect rect = rc;
1967 pDC->FillSolidRect(rc, crBkgnd);
1968 // now draw some faint text patterns
1969 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1970 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1971 DrawBlockLine(pDC, rc, nLineIndex);
1972 return;
1975 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1976 CString sLine = GetLineChars(nLineIndex);
1977 if (sLine.IsEmpty())
1979 pDC->FillSolidRect(rc, crBkgnd);
1980 DrawBlockLine(pDC, rc, nLineIndex);
1981 DrawLineEnding(pDC, rc, nLineIndex, origin);
1982 return;
1985 CheckOtherView();
1987 // Draw the line
1989 pDC->SelectObject(GetFont(FALSE, FALSE));
1991 DrawTextLine(pDC, rc, nLineIndex, origin);
1993 // draw white space after the end of line
1994 CRect frect = rc;
1995 if (origin.x > frect.left)
1996 frect.left = origin.x;
1997 if (frect.right > frect.left)
1998 pDC->FillSolidRect(frect, crBkgnd);
2000 // draw the whitespace chars
2001 LPCTSTR pszChars = (LPCWSTR)sLine;
2002 if (m_bViewWhitespace)
2004 int xpos = 0;
2005 int nChars = 0;
2006 LPCTSTR pLastSpace = pszChars;
2007 int y = rc.top + (rc.bottom-rc.top)/2;
2008 xpos -= m_nOffsetChar * GetCharWidth();
2010 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
2011 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
2012 while (*pszChars)
2014 switch (*pszChars)
2016 case '\t':
2018 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2019 pLastSpace = pszChars + 1;
2020 // draw an arrow
2021 int nSpaces = GetTabSize() - nChars % GetTabSize();
2022 if (xpos + nSpaces * GetCharWidth() > 0)
2024 int xposreal = max(xpos, 0);
2025 if ((xposreal > 0) || (nSpaces > 0))
2027 CPen * oldPen = pDC->SelectObject(&pen);
2028 pDC->MoveTo(xposreal + rc.left, y);
2029 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2030 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y - 4);
2031 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left - 2, y);
2032 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left - 6, y + 4);
2033 pDC->SelectObject(oldPen);
2036 xpos += nSpaces * GetCharWidth();
2037 nChars += nSpaces;
2039 break;
2040 case ' ':
2042 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2043 pLastSpace = pszChars + 1;
2044 // draw a small dot
2045 if (xpos >= 0)
2047 CPen * oldPen = pDC->SelectObject(&pen2);
2048 pDC->MoveTo(xpos + rc.left + GetCharWidth()/2-1, y);
2049 pDC->LineTo(xpos + rc.left + GetCharWidth()/2+1, y);
2050 pDC->SelectObject(oldPen);
2052 xpos += GetCharWidth();
2053 nChars++;
2055 break;
2056 default:
2057 nChars++;
2058 break;
2060 pszChars++;
2063 DrawBlockLine(pDC, rc, nLineIndex);
2064 if (origin.x >= rc.left)
2065 DrawLineEnding(pDC, rc, nLineIndex, origin);
2068 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2070 if (nCount <= 0)
2072 line = _T("");
2073 return;
2076 int nTabSize = GetTabSize();
2078 int nActualOffset = CountExpandedChars(sLine, nOffset);
2080 LPCTSTR pszChars = (LPCWSTR)sLine;
2081 pszChars += nOffset;
2082 int nLength = nCount;
2084 int nTabCount = 0;
2085 for (int i=0; i<nLength; i++)
2087 if (pszChars[i] == _T('\t'))
2088 nTabCount ++;
2091 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2092 int nCurPos = 0;
2093 if (nTabCount > 0 || m_bViewWhitespace)
2095 for (int i=0; i<nLength; i++)
2097 if (pszChars[i] == _T('\t'))
2099 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2100 while (nSpaces > 0)
2102 pszBuf[nCurPos ++] = _T(' ');
2103 nSpaces --;
2106 else
2108 pszBuf[nCurPos] = pszChars[i];
2109 nCurPos ++;
2113 else
2115 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2116 nCurPos = nLength;
2118 pszBuf[nCurPos] = 0;
2119 line.ReleaseBuffer();
2122 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2124 CString sRet;
2125 int nLength = sLine.GetLength();
2126 ExpandChars(sLine, nOffset, nLength, sRet);
2127 return sRet;
2130 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2132 int nTabSize = GetTabSize();
2134 int nActualOffset = 0;
2135 for (int i=0; i<nLength; i++)
2137 if (sLine[i] == _T('\t'))
2138 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2139 else
2140 nActualOffset ++;
2142 return nActualOffset;
2145 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2147 if (m_pwndLeft)
2148 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2149 if (m_pwndRight)
2150 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2151 if (m_pwndBottom)
2152 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2153 if (m_pwndLocator)
2154 m_pwndLocator->Invalidate();
2157 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2159 //almost the same as ScrollAllToLine, but try to put the line in the
2160 //middle of the view, not on top
2161 int nNewTopLine = nNewLine - GetScreenLines()/2;
2162 if (nNewTopLine < 0)
2163 nNewTopLine = 0;
2164 if (nNewTopLine >= (int)m_Screen2View.size())
2165 nNewTopLine = (int)m_Screen2View.size()-1;
2166 if (bAll)
2167 ScrollAllToLine(nNewTopLine);
2168 else
2169 ScrollToLine(nNewTopLine);
2172 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2174 return TRUE;
2177 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2179 if (CView::OnCreate(lpCreateStruct) == -1)
2180 return -1;
2182 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2183 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2184 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2185 m_lfBaseFont.lfHeight = 0;
2186 m_lfBaseFont.lfWeight = FW_NORMAL;
2187 m_lfBaseFont.lfItalic = FALSE;
2188 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2189 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2190 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2191 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2192 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2194 return 0;
2197 void CBaseView::OnDestroy()
2199 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2201 m_pFindDialog->SendMessage(WM_CLOSE);
2202 return;
2204 CView::OnDestroy();
2205 DeleteFonts();
2206 ReleaseBitmap();
2209 void CBaseView::OnSize(UINT nType, int cx, int cy)
2211 CView::OnSize(nType, cx, cy);
2212 ReleaseBitmap();
2214 m_nScreenLines = -1;
2215 m_nScreenChars = -1;
2216 if (m_nLastScreenChars != GetScreenChars())
2218 BuildAllScreen2ViewVector();
2219 m_nLastScreenChars = m_nScreenChars;
2220 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2222 // if we're in wrap mode, the line wrapping most likely changed
2223 // and that means we have to redraw the whole window, not just the
2224 // scrolled part.
2225 Invalidate(FALSE);
2227 else
2229 // make sure the view header is redrawn
2230 CRect rcScroll;
2231 GetClientRect(&rcScroll);
2232 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2233 InvalidateRect(&rcScroll, FALSE);
2236 else
2238 // make sure the view header is redrawn
2239 CRect rcScroll;
2240 GetClientRect(&rcScroll);
2241 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2242 InvalidateRect(&rcScroll, FALSE);
2244 UpdateLocator();
2245 RecalcVertScrollBar();
2246 RecalcHorzScrollBar();
2248 UpdateCaret();
2251 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2253 if (m_pwndLeft)
2254 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2255 if (m_pwndRight)
2256 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2257 if (m_pwndBottom)
2258 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2259 if (m_pwndLocator)
2260 m_pwndLocator->Invalidate();
2261 return CView::OnMouseWheel(nFlags, zDelta, pt);
2264 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2266 if (m_pwndLeft)
2267 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2268 if (m_pwndRight)
2269 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2270 if (m_pwndBottom)
2271 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2272 if (m_pwndLocator)
2273 m_pwndLocator->Invalidate();
2276 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2278 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2279 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2281 if (bControl || bShift)
2283 if (m_pMainFrame->m_bWrapLines)
2284 return;
2285 // Ctrl-Wheel scrolls sideways
2286 ScrollSide(-zDelta/30);
2288 else
2290 ScrollVertical(zDelta);
2294 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2296 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2297 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2299 if (bControl || bShift)
2301 ScrollVertical(zDelta);
2303 else
2305 if (m_pMainFrame->m_bWrapLines)
2306 return;
2307 // Ctrl-Wheel scrolls sideways
2308 ScrollSide(-zDelta/30);
2312 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2314 if (nHitTest == HTCLIENT)
2316 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2318 if (m_nMouseLine < (int)m_Screen2View.size())
2320 if (m_nMouseLine >= 0)
2322 int viewLine = GetViewLineForScreen(m_nMouseLine);
2323 if (viewLine < m_pViewData->GetCount())
2325 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2327 ::SetCursor(::LoadCursor(NULL, IDC_HAND));
2328 return TRUE;
2334 if (m_mouseInMargin)
2336 ::SetCursor(m_margincursor);
2337 return TRUE;
2339 if (m_nMouseLine >= 0)
2341 ::SetCursor(::LoadCursor(NULL, IDC_IBEAM)); // Set To Edit Cursor
2342 return TRUE;
2345 ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); // Set To Arrow Cursor
2346 return TRUE;
2348 return CView::OnSetCursor(pWnd, nHitTest, message);
2351 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2353 CView::OnKillFocus(pNewWnd);
2354 m_bFocused = FALSE;
2355 UpdateCaret();
2356 Invalidate();
2359 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2361 CView::OnSetFocus(pOldWnd);
2362 m_bFocused = TRUE;
2363 UpdateCaret();
2364 Invalidate();
2367 int CBaseView::GetLineFromPoint(CPoint point)
2369 ScreenToClient(&point);
2370 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2373 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2375 if (!this->IsWindowVisible())
2376 return;
2378 CIconMenu popup;
2379 if (!popup.CreatePopupMenu())
2380 return;
2382 AddContextItems(popup, state);
2384 CMenu popupEols;
2385 CMenu popupUnicode;
2386 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2387 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2388 if (IsWritable())
2390 CString temp;
2391 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2392 temp.LoadString(IDS_EDIT_TAB2SPACE);
2393 popup.AppendMenu((MF_STRING | oWhites.HasTabsToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2394 temp.LoadString(IDS_EDIT_SPACE2TAB);
2395 popup.AppendMenu((MF_STRING | oWhites.HasSpacesToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2396 temp.LoadString(IDS_EDIT_TRIM);
2397 popup.AppendMenu((MF_STRING | oWhites.HasTrailWhiteChars) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2399 // add eol submenu
2400 if (!popupEols.CreatePopupMenu())
2401 return;
2403 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2404 for (int i = 1; i < _countof(eolArray); i++)
2406 temp = GetEolName(eolArray[i]);
2407 bool bChecked = (eEolType == eolArray[i]);
2408 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2411 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2412 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2414 // add encoding submenu
2415 if (!popupUnicode.CreatePopupMenu())
2416 return;
2417 for (int i = 0; i < _countof(uctArray); i++)
2419 temp = CFileTextLines::GetEncodingName(uctArray[i]);
2420 bool bChecked = (m_texttype == uctArray[i]);
2421 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2423 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2424 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2428 CompensateForKeyboard(point);
2430 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2431 ResetUndoStep();
2432 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2434 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2436 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2438 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2439 SaveUndoStep();
2441 switch (cmd)
2443 // 2-pane view commands; target is right view
2444 case POPUPCOMMAND_USELEFTBLOCK:
2445 m_pwndRight->UseLeftBlock();
2446 break;
2447 case POPUPCOMMAND_USELEFTFILE:
2448 m_pwndRight->UseLeftFile();
2449 break;
2450 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2451 m_pwndRight->UseBothLeftFirst();
2452 break;
2453 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2454 m_pwndRight->UseBothRightFirst();
2455 break;
2456 case POPUPCOMMAND_MARKBLOCK:
2457 m_pwndRight->MarkBlock(true);
2458 break;
2459 case POPUPCOMMAND_UNMARKBLOCK:
2460 m_pwndRight->MarkBlock(false);
2461 break;
2462 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS:
2463 m_pwndRight->LeaveOnlyMarkedBlocks();
2464 break;
2465 // 2-pane view multiedit commands; target is left view
2466 case POPUPCOMMAND_PREPENDFROMRIGHT:
2467 if (!m_pwndLeft->IsReadonly())
2468 m_pwndLeft->UseBothRightFirst();
2469 break;
2470 case POPUPCOMMAND_REPLACEBYRIGHT:
2471 if (!m_pwndLeft->IsReadonly())
2472 m_pwndLeft->UseRightBlock();
2473 break;
2474 case POPUPCOMMAND_APPENDFROMRIGHT:
2475 if (!m_pwndLeft->IsReadonly())
2476 m_pwndLeft->UseBothLeftFirst();
2477 break;
2478 case POPUPCOMMAND_USERIGHTFILE:
2479 m_pwndLeft->UseRightFile();
2480 break;
2481 // 3-pane view commands; target is bottom view
2482 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2483 m_pwndBottom->UseBothRightFirst();
2484 break;
2485 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2486 m_pwndBottom->UseBothLeftFirst();
2487 break;
2488 case POPUPCOMMAND_USEYOURBLOCK:
2489 m_pwndBottom->UseRightBlock();
2490 break;
2491 case POPUPCOMMAND_USEYOURFILE:
2492 m_pwndBottom->UseRightFile();
2493 break;
2494 case POPUPCOMMAND_USETHEIRBLOCK:
2495 m_pwndBottom->UseLeftBlock();
2496 break;
2497 case POPUPCOMMAND_USETHEIRFILE:
2498 m_pwndBottom->UseLeftFile();
2499 break;
2500 // copy, cut and paste commands
2501 case ID_EDIT_COPY:
2502 OnEditCopy();
2503 break;
2504 case ID_EDIT_CUT:
2505 OnEditCut();
2506 break;
2507 case ID_EDIT_PASTE:
2508 OnEditPaste();
2509 break;
2510 // white chars manipulations
2511 case POPUPCOMMAND_TABTOSPACES:
2512 ConvertTabToSpaces();
2513 break;
2514 case POPUPCOMMAND_SPACESTOTABS:
2515 Tabularize();
2516 break;
2517 case POPUPCOMMAND_REMOVETRAILWHITES:
2518 RemoveTrailWhiteChars();
2519 break;
2520 default:
2521 return;
2522 } // switch (cmd)
2523 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2524 return;
2527 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2529 if (!m_pViewData)
2530 return;
2532 int nViewBlockStart = -1;
2533 int nViewBlockEnd = -1;
2534 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2535 if ((point.x >= 0) && (point.y >= 0))
2537 int nLine = GetLineFromPoint(point)-1;
2538 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2540 int nViewLine = GetViewLineForScreen(nLine);
2541 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2543 ClearSelection(); // Clear text-copy selection
2545 nViewBlockStart = nViewLine;
2546 nViewBlockEnd = nViewLine;
2547 DiffStates state = m_pViewData->GetState(nViewLine);
2548 while (nViewBlockStart > 0)
2550 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2551 if (!LinesInOneChange(-1, state, lineState))
2552 break;
2553 nViewBlockStart--;
2556 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2558 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2559 if (!LinesInOneChange(1, state, lineState))
2560 break;
2561 nViewBlockEnd++;
2564 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2565 UpdateCaretPosition(point);
2570 // FixSelection(); fix selection range
2571 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2572 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2574 DiffStates state = DIFFSTATE_UNKNOWN;
2575 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2577 // find a more 'relevant' state in the selection
2578 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2580 state = m_pViewData->GetState(i);
2581 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2582 break;
2585 OnContextMenu(point, state);
2588 void CBaseView::RefreshViews()
2590 if (m_pwndLeft)
2592 m_pwndLeft->UpdateStatusBar();
2593 m_pwndLeft->Invalidate();
2595 if (m_pwndRight)
2597 m_pwndRight->UpdateStatusBar();
2598 m_pwndRight->Invalidate();
2600 if (m_pwndBottom)
2602 m_pwndBottom->UpdateStatusBar();
2603 m_pwndBottom->Invalidate();
2605 if (m_pwndLocator)
2606 m_pwndLocator->Invalidate();
2609 void CBaseView::GoToFirstDifference()
2611 SetCaretToFirstViewLine();
2612 SelectNextBlock(1, false, false);
2615 void CBaseView::GoToFirstConflict()
2617 SetCaretToFirstViewLine();
2618 SelectNextBlock(1, true, false);
2621 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2623 ClearSelection();
2624 SetupAllSelection(nStart, max(nStart, nEnd));
2626 UpdateCaretPosition(SetupPoint(0, nStart));
2627 Invalidate();
2630 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2632 ClearSelection();
2633 SetupAllViewSelection(nStart, max(nStart, nEnd));
2635 UpdateCaretViewPosition(SetupPoint(0, nStart));
2636 Invalidate();
2639 void CBaseView::SetupAllViewSelection(int start, int end)
2641 SetupViewSelection(m_pwndBottom, start, end);
2642 SetupViewSelection(m_pwndLeft, start, end);
2643 SetupViewSelection(m_pwndRight, start, end);
2646 void CBaseView::SetupAllSelection(int start, int end)
2648 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2651 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2653 void CBaseView::SetupSelection(int start, int end)
2655 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2658 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2660 if (!IsViewGood(view))
2661 return;
2662 view->SetupViewSelection(start, end);
2665 void CBaseView::SetupViewSelection(int start, int end)
2667 // clear text selection before setting line selection ?
2668 m_nSelViewBlockStart = start;
2669 m_nSelViewBlockEnd = end;
2670 Invalidate();
2674 void CBaseView::OnMergePreviousconflict()
2676 SelectNextBlock(-1, true);
2679 void CBaseView::OnMergeNextconflict()
2681 SelectNextBlock(1, true);
2684 void CBaseView::OnMergeNextdifference()
2686 SelectNextBlock(1, false);
2689 void CBaseView::OnMergePreviousdifference()
2691 SelectNextBlock(-1, false);
2694 bool CBaseView::HasNextConflict()
2696 return SelectNextBlock(1, true, true, true);
2699 bool CBaseView::HasPrevConflict()
2701 return SelectNextBlock(-1, true, true, true);
2704 bool CBaseView::HasNextDiff()
2706 return SelectNextBlock(1, false, true, true);
2709 bool CBaseView::HasPrevDiff()
2711 return SelectNextBlock(-1, false, true, true);
2714 bool CBaseView::LinesInOneChange(int direction,
2715 DiffStates initialLineState, DiffStates currentLineState)
2717 // Checks whether all the adjacent lines starting from the initial line
2718 // and up to the current line form the single change
2720 // First of all, if the two lines have identical states, they surely
2721 // belong to one change.
2722 if (initialLineState == currentLineState)
2723 return true;
2725 // Either we move down and initial line state is "added" or "removed" and
2726 // current line state is "empty"...
2727 if (direction > 0)
2729 if (currentLineState == DIFFSTATE_EMPTY)
2731 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2732 return true;
2734 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2735 return true;
2737 // ...or we move up and initial line state is "empty" and current line
2738 // state is "added" or "removed".
2739 if (direction < 0)
2741 if (initialLineState == DIFFSTATE_EMPTY)
2743 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2744 return true;
2746 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2747 return true;
2749 return false;
2752 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2754 if (! m_pViewData)
2755 return false;
2757 const int linesCount = (int)m_Screen2View.size();
2758 if(linesCount == 0)
2759 return false;
2761 int nCenterPos = GetCaretPosition().y;
2762 int nLimit = -1;
2763 if (nDirection > 0)
2764 nLimit = linesCount;
2766 if (nCenterPos >= linesCount)
2767 nCenterPos = linesCount-1;
2769 if (bSkipEndOfCurrentBlock)
2771 // Find end of current block
2772 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2773 while (nCenterPos != nLimit)
2775 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2776 if (!LinesInOneChange(nDirection, state, lineState))
2777 break;
2778 nCenterPos += nDirection;
2782 // Find next diff/conflict block
2783 while (nCenterPos != nLimit)
2785 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2786 if (!bConflict &&
2787 (linestate != DIFFSTATE_NORMAL) &&
2788 (linestate != DIFFSTATE_UNKNOWN))
2790 break;
2792 if (bConflict &&
2793 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2794 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2795 (linestate == DIFFSTATE_CONFLICTED) ||
2796 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2798 break;
2801 nCenterPos += nDirection;
2803 if (nCenterPos == nLimit)
2804 return false;
2805 if (dryrun)
2806 return (nCenterPos != nLimit);
2808 // Find end of new block
2809 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2810 int nBlockEnd = nCenterPos;
2811 const int maxAllowedLine = nLimit-nDirection;
2812 while (nBlockEnd != maxAllowedLine)
2814 const int lineIndex = nBlockEnd + nDirection;
2815 if (lineIndex >= linesCount)
2816 break;
2817 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2818 if (!LinesInOneChange(nDirection, state, lineState))
2819 break;
2820 nBlockEnd += nDirection;
2823 int nTopPos = nCenterPos - (GetScreenLines()/2);
2824 if (nTopPos < 0)
2825 nTopPos = 0;
2827 POINT ptCaretPos = {0, nCenterPos};
2828 SetCaretPosition(ptCaretPos);
2829 ClearSelection();
2830 if (nDirection > 0)
2831 SetupAllSelection(nCenterPos, nBlockEnd);
2832 else
2833 SetupAllSelection(nBlockEnd, nCenterPos);
2835 ScrollAllToLine(nTopPos, FALSE);
2836 RecalcAllVertScrollBars(TRUE);
2837 SetCaretToLineStart();
2838 EnsureCaretVisible();
2839 OnNavigateNextinlinediff();
2841 UpdateViewsCaretPosition();
2842 UpdateCaret();
2843 ShowDiffLines(nCenterPos);
2844 return true;
2847 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2849 if (pNMHDR->idFrom != (UINT_PTR)m_hWnd)
2850 return FALSE;
2852 CString strTipText;
2853 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2855 DWORD pos = GetMessagePos();
2856 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2857 ScreenToClient(&point);
2858 const int nLine = GetButtonEventLineIndex(point);
2860 if (nLine >= 0)
2862 int nViewLine = GetViewLineForScreen(nLine);
2863 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2865 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2866 if (movedIndex >= 0)
2868 if (m_pViewData->IsMovedFrom(nViewLine))
2870 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2872 else
2874 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2881 *pResult = 0;
2882 if (strTipText.IsEmpty())
2883 return TRUE;
2885 // need to handle both ANSI and UNICODE versions of the message
2886 if (pNMHDR->code == TTN_NEEDTEXTA)
2888 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2889 pTTTA->lpszText = m_szTip;
2890 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2892 else
2894 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2895 lstrcpyn(m_wszTip, strTipText, min(strTipText.GetLength() + 1, _countof(m_wszTip) - 1));
2896 pTTTW->lpszText = m_wszTip;
2899 return TRUE; // message was handled
2902 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2904 CRect rcClient;
2905 GetClientRect(rcClient);
2906 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2907 int marginwidth = MARGINWIDTH;
2908 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2910 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2912 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2914 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2916 // inside the header part of the view (showing the filename)
2917 pTI->hwnd = this->m_hWnd;
2918 this->GetClientRect(&pTI->rect);
2919 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2920 pTI->uId = (UINT_PTR)m_hWnd;
2921 pTI->lpszText = LPSTR_TEXTCALLBACK;
2923 // we want multi line tooltips
2924 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2925 if (pToolTip->GetSafeHwnd() != NULL)
2927 pToolTip->SetMaxTipWidth(INT_MAX);
2930 return (textrect.PtInRect(point) ? 1 : 2);
2933 return -1;
2936 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2938 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2939 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2941 switch (nChar)
2943 case VK_TAB:
2944 if (bControl)
2946 if (this==m_pwndLeft)
2948 if (IsViewGood(m_pwndRight))
2950 m_pwndRight->SetFocus();
2952 else if (IsViewGood(m_pwndBottom))
2954 m_pwndBottom->SetFocus();
2957 else if (this==m_pwndRight)
2959 if (IsViewGood(m_pwndBottom))
2961 m_pwndBottom->SetFocus();
2963 else if (IsViewGood(m_pwndLeft))
2965 m_pwndLeft->SetFocus();
2968 else if (this==m_pwndBottom)
2970 if (IsViewGood(m_pwndLeft))
2972 m_pwndLeft->SetFocus();
2974 else if (IsViewGood(m_pwndRight))
2976 m_pwndRight->SetFocus();
2980 break;
2981 case VK_PRIOR:
2983 POINT ptCaretPos = GetCaretPosition();
2984 ptCaretPos.y -= GetScreenLines();
2985 ptCaretPos.y = max(ptCaretPos.y, 0);
2986 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2987 SetCaretPosition(ptCaretPos);
2988 OnCaretMove(MOVELEFT, bShift);
2989 ShowDiffLines(ptCaretPos.y);
2991 break;
2992 case VK_NEXT:
2994 POINT ptCaretPos = GetCaretPosition();
2995 ptCaretPos.y += GetScreenLines();
2996 if (ptCaretPos.y >= GetLineCount())
2997 ptCaretPos.y = GetLineCount()-1;
2998 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2999 SetCaretPosition(ptCaretPos);
3000 OnCaretMove(MOVERIGHT, bShift);
3001 ShowDiffLines(ptCaretPos.y);
3003 break;
3004 case VK_HOME:
3006 if (bControl)
3008 ScrollAllToLine(0);
3009 SetCaretToViewStart();
3010 m_nCaretGoalPos = 0;
3011 if (bShift)
3012 AdjustSelection(MOVELEFT);
3013 else
3014 ClearSelection();
3015 UpdateCaret();
3017 else
3019 POINT ptCaretPos = GetCaretPosition();
3020 CString sLine = GetLineChars(ptCaretPos.y);
3021 int pos = 0;
3022 while (pos < sLine.GetLength())
3024 if (sLine[pos] != ' ' && sLine[pos] != '\t')
3025 break;
3026 ++pos;
3028 if (ptCaretPos.x == pos)
3030 SetCaretToLineStart();
3031 m_nCaretGoalPos = 0;
3032 OnCaretMove(MOVERIGHT, bShift);
3033 ScrollAllToChar(0);
3035 else
3037 ptCaretPos.x = pos;
3038 SetCaretAndGoalPosition(ptCaretPos);
3039 OnCaretMove(MOVELEFT, bShift);
3043 break;
3044 case VK_END:
3046 if (bControl)
3048 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3049 POINT ptCaretPos;
3050 ptCaretPos.y = GetLineCount()-1;
3051 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3052 SetCaretAndGoalPosition(ptCaretPos);
3053 if (bShift)
3054 AdjustSelection(MOVERIGHT);
3055 else
3056 ClearSelection();
3058 else
3060 POINT ptCaretPos = GetCaretPosition();
3061 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3062 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3064 ptCaretPos.x--;
3066 SetCaretAndGoalPosition(ptCaretPos);
3067 OnCaretMove(MOVERIGHT, bShift);
3070 break;
3071 case VK_BACK:
3072 if (IsWritable())
3074 if (! HasTextSelection())
3076 POINT ptCaretPos = GetCaretPosition();
3077 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3078 break;
3079 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3080 if (bControl)
3081 MoveCaretWordLeft();
3082 else
3084 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3088 m_ptSelectionViewPosStart = GetCaretViewPosition();
3090 RemoveSelectedText();
3092 break;
3093 case VK_DELETE:
3094 if (IsWritable())
3096 if (! HasTextSelection())
3098 if (bControl)
3100 m_ptSelectionViewPosStart = GetCaretViewPosition();
3101 MoveCaretWordRight();
3102 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3104 else
3106 if (! MoveCaretRight())
3107 break;
3108 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3109 MoveCaretLeft();
3110 m_ptSelectionViewPosStart = GetCaretViewPosition();
3113 RemoveSelectedText();
3115 break;
3116 case VK_INSERT:
3117 m_bInsertMode = !m_bInsertMode;
3118 UpdateCaret();
3119 break;
3121 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3124 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3126 const int nClickedLine = GetButtonEventLineIndex(point);
3127 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3129 POINT ptCaretPos;
3130 ptCaretPos.y = nClickedLine;
3131 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3132 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3133 SetCaretAndGoalPosition(ptCaretPos);
3135 if (nFlags & MK_SHIFT)
3136 AdjustSelection(MOVERIGHT);
3137 else
3139 ClearSelection();
3140 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3141 if (point.x < GetMarginWidth())
3143 // select the whole line
3144 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3145 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3149 UpdateViewsCaretPosition();
3150 Invalidate();
3153 CView::OnLButtonDown(nFlags, point);
3156 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3158 if (zChar == ' ' || zChar == '\t' )
3160 return CHG_WHITESPACE;
3162 if (zChar < 0x20)
3164 return CHG_CONTROL;
3166 if (m_sWordSeparators.Find(zChar) >= 0)
3168 return CHG_WORDSEPARATOR;
3170 return CHG_WORDLETTER;
3173 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3175 if (m_pViewData == 0) {
3176 CView::OnLButtonDblClk(nFlags, point);
3177 return;
3180 const int nClickedLine = GetButtonEventLineIndex(point);
3181 if ( nClickedLine < 0)
3182 return;
3183 int nViewLine = GetViewLineForScreen(nClickedLine);
3184 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3186 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3188 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3190 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3191 int screenLine = FindViewLineNumber(movedindex);
3192 int nTop = screenLine - GetScreenLines()/2;
3193 if (nTop < 0)
3194 nTop = 0;
3195 ScrollAllToLine(nTop);
3196 // find and select the whole moved block
3197 int startSel = movedindex;
3198 int endSel = movedindex;
3199 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3200 startSel--;
3201 startSel++;
3202 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3203 endSel++;
3204 endSel--;
3205 m_pOtherView->SetupSelection(startSel, endSel);
3206 return CView::OnLButtonDblClk(nFlags, point);
3210 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3212 // a double click on a marker expands the hidden text
3213 int i = nViewLine;
3214 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3216 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3217 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3218 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3219 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3220 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3221 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3222 i++;
3224 BuildAllScreen2ViewVector();
3225 if (m_pwndLeft)
3226 m_pwndLeft->Invalidate();
3227 if (m_pwndRight)
3228 m_pwndRight->Invalidate();
3229 if (m_pwndBottom)
3230 m_pwndBottom->Invalidate();
3232 else
3234 POINT ptCaretPos;
3235 ptCaretPos.y = nClickedLine;
3236 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3237 SetCaretPosition(ptCaretPos);
3238 ClearSelection();
3240 POINT ptViewCarret = GetCaretViewPosition();
3241 nViewLine = ptViewCarret.y;
3242 if (nViewLine >= GetViewCount())
3243 return;
3244 const CString &sLine = GetViewLine(nViewLine);
3245 int nLineLength = sLine.GetLength();
3246 int nBasePos = ptViewCarret.x;
3247 // get target char group
3248 ECharGroup eLeft = CHG_UNKNOWN;
3249 if (nBasePos > 0)
3251 eLeft = GetCharGroup(sLine[nBasePos-1]);
3253 ECharGroup eRight = CHG_UNKNOWN;
3254 if (nBasePos < nLineLength)
3256 eRight = GetCharGroup(sLine[nBasePos]);
3258 ECharGroup eTarget = max(eRight, eLeft);
3259 // find left margin
3260 int nLeft = nBasePos;
3261 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3263 nLeft--;
3265 // get right margin
3266 int nRight = nBasePos;
3267 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3269 nRight++;
3271 // set selection
3272 m_ptSelectionViewPosStart.x = nLeft;
3273 m_ptSelectionViewPosStart.y = nViewLine;
3274 m_ptSelectionViewPosEnd.x = nRight;
3275 m_ptSelectionViewPosEnd.y = nViewLine;
3276 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3277 SetupAllViewSelection(nViewLine, nViewLine);
3278 // set caret
3279 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3280 UpdateViewsCaretPosition();
3281 UpdateGoalPos();
3283 // set mark word
3284 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3285 int nMarkWidth = max(nRight - nLeft, 0);
3286 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3287 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3289 m_sMarkedWord.Empty();
3292 if (m_pwndLeft)
3293 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3294 if (m_pwndRight)
3295 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3296 if (m_pwndBottom)
3297 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3299 Invalidate();
3300 if (m_pwndLocator)
3301 m_pwndLocator->Invalidate();
3304 CView::OnLButtonDblClk(nFlags, point);
3307 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3309 const int nClickedLine = GetButtonEventLineIndex(point);
3310 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3312 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3314 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3315 if (pidl)
3317 SHOpenFolderAndSelectItems(pidl,0,0,0);
3318 CoTaskMemFree((LPVOID)pidl);
3321 return;
3323 POINT ptCaretPos;
3324 ptCaretPos.y = nClickedLine;
3325 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3326 SetCaretAndGoalPosition(ptCaretPos);
3327 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3328 if (m_pwndLeft)
3329 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3330 if (m_pwndRight)
3331 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3332 if (m_pwndBottom)
3333 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3334 ClearSelection();
3335 m_ptSelectionViewPosStart.x = 0;
3336 m_ptSelectionViewPosStart.y = nClickedLine;
3337 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3338 m_ptSelectionViewPosEnd.y = nClickedLine;
3339 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3340 UpdateViewsCaretPosition();
3341 Invalidate();
3342 if (m_pwndLocator)
3343 m_pwndLocator->Invalidate();
3346 void CBaseView::OnEditCopy()
3348 CString sCopyData = GetSelectedText();
3350 if (!sCopyData.IsEmpty())
3352 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3356 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3358 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3360 --m_pMainFrame->m_nMoveMovesToIgnore;
3361 CView::OnMouseMove(nFlags, point);
3362 return;
3364 int nMouseLine = GetButtonEventLineIndex(point);
3365 if (nMouseLine < -1)
3366 nMouseLine = -1;
3367 m_mouseInMargin = point.x < GetMarginWidth();
3369 ShowDiffLines(nMouseLine);
3371 KillTimer(IDT_SCROLLTIMER);
3372 if (nFlags & MK_LBUTTON)
3374 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3375 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3376 if (saveMouseLine < 0)
3377 return;
3378 int col = CalcColFromPoint(point.x, saveMouseLine);
3379 int charIndex = CalculateCharIndex(saveMouseLine, col);
3380 if (HasSelection() &&
3381 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3383 POINT ptCaretPos = {charIndex, nMouseLine};
3384 SetCaretAndGoalPosition(ptCaretPos);
3385 AdjustSelection(MOVERIGHT);
3386 Invalidate();
3387 UpdateWindow();
3389 if (nMouseLine < m_nTopLine)
3391 ScrollAllToLine(m_nTopLine-1, TRUE);
3392 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3394 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3396 ScrollAllToLine(m_nTopLine+1, TRUE);
3397 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3399 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3401 ScrollAllSide(-1);
3402 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3404 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3406 ScrollAllSide(1);
3407 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3409 SetCapture();
3413 CView::OnMouseMove(nFlags, point);
3416 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3418 ShowDiffLines(-1);
3419 ReleaseCapture();
3420 KillTimer(IDT_SCROLLTIMER);
3422 __super::OnLButtonUp(nFlags, point);
3425 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3427 if (nIDEvent == IDT_SCROLLTIMER)
3429 POINT point;
3430 GetCursorPos(&point);
3431 ScreenToClient(&point);
3432 int nMouseLine = GetButtonEventLineIndex(point);
3433 if (nMouseLine < -1)
3435 nMouseLine = -1;
3437 if (GetKeyState(VK_LBUTTON)&0x8000)
3439 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3440 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3441 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3442 if (nMouseLine < m_nTopLine)
3444 ScrollAllToLine(m_nTopLine-1, TRUE);
3445 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3447 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3449 ScrollAllToLine(m_nTopLine+1, TRUE);
3450 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3452 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3454 ScrollAllSide(-1);
3455 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3457 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3459 ScrollAllSide(1);
3460 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3466 CView::OnTimer(nIDEvent);
3469 void CBaseView::ShowDiffLines(int nLine)
3471 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3473 m_pwndLineDiffBar->ShowLines(nLine);
3474 nLine = -1;
3475 m_nMouseLine = nLine;
3476 return;
3479 if ((!m_pwndRight)||(!m_pwndLeft))
3480 return;
3481 if(m_pMainFrame->m_bOneWay)
3482 return;
3484 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3485 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3487 if (nLine < 0)
3488 return;
3490 if (nLine != m_nMouseLine)
3492 if (nLine >= GetLineCount())
3493 nLine = -1;
3494 m_nMouseLine = nLine;
3495 m_pwndLineDiffBar->ShowLines(nLine);
3497 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3500 const viewdata& CBaseView::GetEmptyLineData()
3502 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3503 return emptyLine;
3506 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3508 for (int i = 0; i < nCount; i++)
3510 InsertViewData(nFirstView, GetEmptyLineData());
3515 void CBaseView::UpdateCaret()
3517 POINT ptCaretPos = GetCaretPosition();
3518 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3519 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3520 SetCaretPosition(ptCaretPos);
3522 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3524 if (m_bFocused &&
3525 ptCaretPos.y >= m_nTopLine &&
3526 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3527 nCaretOffset >= m_nOffsetChar &&
3528 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3530 POINT pt1 = TextToClient(ptCaretPos);
3531 if (m_bInsertMode)
3532 CreateSolidCaret(2, GetLineHeight());
3533 else
3535 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3536 POINT pt2 = TextToClient(pt);
3537 int width = max(GetCharWidth(), pt2.x - pt1.x);
3538 CreateSolidCaret(width, GetLineHeight());
3540 SetCaretPos(pt1);
3541 ShowCaret();
3543 else
3545 HideCaret();
3549 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3551 POINT ptViewPos;
3552 ptViewPos.x = pt.x;
3554 int nSubLine = GetSubLineOffset(pt.y);
3555 if (nSubLine > 0)
3557 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3559 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3563 ptViewPos.y = GetViewLineForScreen(pt.y);
3564 return ptViewPos;
3567 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3569 POINT ptPos;
3570 int nViewLineLenLeft = GetViewLineLength(pt.y);
3571 ptPos.x = min(nViewLineLenLeft, pt.x);
3572 ptPos.y = FindScreenLineForViewLine(pt.y);
3573 if (GetViewLineForScreen(ptPos.y) != pt.y )
3575 ptPos.x = 0;
3577 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3579 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3580 while (nSubLineLength < ptPos.x)
3582 ptPos.x -= nSubLineLength;
3583 nViewLineLenLeft -= nSubLineLength;
3584 ptPos.y++;
3585 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3587 // last pos of non last sub-line go to start of next screen line
3588 // Note: while this works correctly, it's not what a user might expect:
3589 // cursor-right when the caret is before the last char of a wrapped line
3590 // now moves the caret to the next line. But users expect the caret to
3591 // move to the right of the last char instead, and with another cursor-right
3592 // keystroke to move the caret to the next line.
3593 // Basically, this would require to handle two caret positions for the same
3594 // logical position in the line string (one on the last position of the first line,
3595 // one on the first position of the new line. For non-wrapped lines this works
3596 // because there's an 'invisible' newline char at the end of the first line.
3597 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3599 ptPos.x = 0;
3600 ptPos.y++;
3604 return ptPos;
3608 void CBaseView::EnsureCaretVisible()
3610 POINT ptCaretPos = GetCaretPosition();
3611 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3613 if (ptCaretPos.y < m_nTopLine)
3614 ScrollAllToLine(ptCaretPos.y);
3615 int screnLines = GetScreenLines();
3616 if (screnLines)
3618 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3619 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3620 if (nCaretOffset < m_nOffsetChar)
3621 ScrollAllToChar(nCaretOffset);
3622 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3623 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3627 int CBaseView::CalculateActualOffset(const POINT& point)
3629 int nLineIndex = point.y;
3630 int nCharIndex = point.x;
3631 ASSERT(nCharIndex >= 0);
3632 CString sLine = GetLineChars(nLineIndex);
3633 int nLineLength = sLine.GetLength();
3634 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3637 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3639 int nLength = GetLineLength(nLineIndex);
3640 int nSubLine = GetSubLineOffset(nLineIndex);
3641 if (nSubLine>=0)
3643 int nViewLine = GetViewLineForScreen(nLineIndex);
3644 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3646 int nMultilineCount = CountMultiLines(nViewLine);
3647 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3649 nLength--;
3653 CString Line = GetLineChars(nLineIndex);
3654 int nIndex = 0;
3655 int nOffset = 0;
3656 int nTabSize = GetTabSize();
3657 while (nOffset < nActualOffset && nIndex < nLength)
3659 if (Line.GetAt(nIndex) == _T('\t'))
3660 nOffset += (nTabSize - nOffset % nTabSize);
3661 else
3662 ++nOffset;
3663 ++nIndex;
3665 return nIndex;
3669 * @param xpos X coordinate in CBaseView
3670 * @param lineIndex logical line index (e.g. wrap/collapse)
3672 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3674 int xpos2;
3675 CDC *pDC = GetDC();
3676 if (pDC != nullptr)
3678 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3679 int fit = text.GetLength();
3680 std::unique_ptr<int> posBuffer(new int[fit]);
3681 pDC->SelectObject(GetFont()); // is this right font ?
3682 SIZE size;
3683 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3684 ReleaseDC(pDC);
3685 int lower = -1, upper = fit - 1;
3686 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3689 int middle = (upper + lower + 1) / 2;
3690 int width = posBuffer.get()[middle];
3691 if (xcheck < width)
3692 upper = middle - 1;
3693 else
3694 lower = middle;
3695 } while (lower < upper);
3696 lower++;
3697 xpos2 = lower;
3698 if (lower < fit - 1)
3700 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3701 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3702 xpos2++;
3705 else
3707 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3708 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3709 xpos2++;
3711 return xpos2;
3714 POINT CBaseView::TextToClient(const POINT& point)
3716 POINT pt;
3717 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3718 pt.y = nOffsetScreenLine * GetLineHeight();
3719 pt.x = CalculateActualOffset(point);
3721 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3722 CDC * pDC = GetDC();
3723 if (pDC)
3725 pDC->SelectObject(GetFont()); // is this right font ?
3726 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3727 CString sLine = GetLineChars(nScreenLine);
3728 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3729 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3730 ReleaseDC(pDC);
3731 } else {
3732 nLeft += pt.x * GetCharWidth();
3735 pt.x = nLeft;
3736 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3737 return pt;
3740 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3742 CView::OnChar(nChar, nRepCnt, nFlags);
3744 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3745 bool bSkipSelectionClear = false;
3747 if (IsReadonly())
3748 return;
3750 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3751 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3753 return;
3756 if (!m_pViewData) // no data - nothing to do
3757 return;
3759 if (nChar == VK_F16)
3761 // generated by a ctrl+backspace - ignore.
3763 else if (nChar==VK_TAB && HasTextLineSelection())
3765 // change indentation for selected lines
3766 if (bShift)
3768 RemoveIndentationForSelectedBlock();
3770 else
3772 AddIndentationForSelectedBlock();
3774 bSkipSelectionClear = true;
3776 else if ((nChar > 31)||(nChar == VK_TAB))
3778 ResetUndoStep();
3779 RemoveSelectedText();
3780 POINT ptCaretViewPos = GetCaretViewPosition();
3781 int nViewLine = ptCaretViewPos.y;
3782 if ((nViewLine==0)&&(GetViewCount()==0))
3783 OnChar(VK_RETURN, 0, 0);
3784 int charCount = 1;
3785 viewdata lineData = GetViewData(nViewLine);
3786 if (nChar == VK_TAB)
3788 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3789 if (indentChars > 0)
3791 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3792 charCount = indentChars;
3794 else
3795 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3797 else
3799 if (m_bInsertMode)
3800 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3801 else
3803 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3804 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3805 else
3806 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3809 if (IsStateEmpty(lineData.state))
3811 // if not last line set EOL
3812 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3814 if (!IsViewLineEmpty(nCheckViewLine))
3816 lineData.ending = m_lineendings;
3817 break;
3820 // make sure previous (non empty) line have EOL set
3821 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3823 if (!IsViewLineEmpty(nCheckViewLine))
3825 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3827 SetViewLineEnding(nCheckViewLine, m_lineendings);
3829 break;
3833 lineData.state = DIFFSTATE_EDITED;
3834 bool bNeedRenumber = false;
3835 if (lineData.linenumber == -1)
3837 lineData.linenumber = 0;
3838 bNeedRenumber = true;
3840 SetViewData(nViewLine, lineData);
3841 SetModified();
3842 SaveUndoStep();
3843 BuildAllScreen2ViewVector(nViewLine);
3844 if (bNeedRenumber)
3846 UpdateViewLineNumbers();
3848 for (int i = 0; i < charCount; ++i)
3849 MoveCaretRight();
3850 UpdateGoalPos();
3852 else if (nChar == 10)
3854 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3855 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3856 EOL newEOL = EOL_CRLF;
3857 switch (eol)
3859 case EOL_CRLF:
3860 newEOL = EOL_CR;
3861 break;
3862 case EOL_CR:
3863 newEOL = EOL_LF;
3864 break;
3865 case EOL_LF:
3866 newEOL = EOL_CRLF;
3867 break;
3869 if (eol==EOL_NOENDING || eol==newEOL)
3870 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3871 // to add EOL on newly edited empty line hit enter
3872 // don't store into UNDO if no change happened
3873 // and don't mark file as modified
3874 return;
3875 AddUndoViewLine(nViewLine);
3876 m_pViewData->SetLineEnding(nViewLine, newEOL);
3877 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3878 UpdateGoalPos();
3880 else if (nChar == VK_RETURN)
3882 // insert a new, fresh and empty line below the cursor
3883 RemoveSelectedText();
3885 CUndo::GetInstance().BeginGrouping();
3887 POINT ptCaretViewPos = GetCaretViewPosition();
3888 int nViewLine = ptCaretViewPos.y;
3889 int nLeft = ptCaretViewPos.x;
3890 CString sLine = GetViewLineChars(nViewLine);
3891 CString sLineLeft = sLine.Left(nLeft);
3892 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3893 EOL eOriginalEnding = EOL_AUTOLINE;
3894 if (m_pViewData->GetCount() > nViewLine)
3895 eOriginalEnding = GetViewLineEnding(nViewLine);
3897 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3899 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3900 SetViewData(nViewLine, newFirstLine);
3903 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3904 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3905 InsertViewData(nInsertLine, newLastLine);
3906 SetModified();
3907 SaveUndoStep();
3909 // adds new line everywhere except me
3910 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3912 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3914 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3916 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3918 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3920 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3922 SaveUndoStep();
3924 UpdateViewLineNumbers();
3925 SaveUndoStep();
3926 CUndo::GetInstance().EndGrouping();
3928 BuildAllScreen2ViewVector();
3929 // move the cursor to the new line
3930 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3931 SetCaretAndGoalViewPosition(ptCaretViewPos);
3933 else
3934 return; // Unknown control character -- ignore it.
3935 if (!bSkipSelectionClear)
3936 ClearSelection();
3937 EnsureCaretVisible();
3938 UpdateCaret();
3939 Invalidate(FALSE);
3942 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3944 ResetUndoStep();
3945 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3946 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3947 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3948 SetModified();
3949 SaveUndoStep();
3950 RecalcAllVertScrollBars();
3951 Invalidate(FALSE);
3954 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3956 if (m_pViewData == NULL)
3957 return;
3958 int viewLine = nViewLineIndex;
3959 EOL ending = m_pViewData->GetLineEnding(viewLine);
3960 if (ending == EOL_NOENDING)
3962 ending = m_lineendings;
3964 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3965 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3967 CString sPartLine = GetViewLineChars(nViewLineIndex);
3968 int nPosx = GetCaretPosition().x; // should be view pos ?
3969 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3970 sPartLine = sPartLine.Mid(nPosx);
3971 newLine.sLine = sPartLine;
3973 m_pViewData->InsertData(viewLine+1, newLine);
3974 BuildAllScreen2ViewVector();
3977 void CBaseView::RemoveSelectedText()
3979 if (m_pViewData == NULL)
3980 return;
3981 if (!HasTextSelection())
3982 return;
3984 // fix selection if starts or ends on empty line
3985 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3986 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3989 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3990 SetCaretViewPosition(m_ptSelectionViewPosStart);
3991 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3994 m_ptSelectionViewPosStart = GetCaretViewPosition();
3995 if (!HasTextSelection())
3997 ClearSelection();
3998 return;
4001 // We want to undo the insertion in a single step.
4002 ResetUndoStep();
4003 CUndo::GetInstance().BeginGrouping();
4005 // combine first and last line
4006 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
4007 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
4008 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
4009 oFirstLine.ending = oLastLine.ending;
4010 oFirstLine.state = DIFFSTATE_EDITED;
4011 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
4013 // clean up middle lines if any
4014 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
4016 viewdata oEmptyLine = GetEmptyLineData();
4017 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
4019 SetViewData(nViewLine, oEmptyLine);
4021 SaveUndoStep();
4023 if (CleanEmptyLines())
4025 BuildAllScreen2ViewVector(); // schedule full rebuild
4027 SaveUndoStep();
4028 UpdateViewLineNumbers();
4031 SetModified(); //TODO set modified only if real data was changed
4032 SaveUndoStep();
4033 CUndo::GetInstance().EndGrouping();
4035 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4036 SetCaretViewPosition(m_ptSelectionViewPosStart);
4037 UpdateGoalPos();
4038 ClearSelection();
4039 UpdateCaret();
4040 EnsureCaretVisible();
4041 Invalidate(FALSE);
4044 void CBaseView::PasteText()
4046 if (!OpenClipboard())
4047 return;
4049 CString sClipboardText;
4050 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4051 if (hglb)
4053 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4054 sClipboardText = CString(lpstr);
4055 GlobalUnlock(hglb);
4057 hglb = GetClipboardData(CF_UNICODETEXT);
4058 if (hglb)
4060 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4061 sClipboardText = lpstr;
4062 GlobalUnlock(hglb);
4064 CloseClipboard();
4066 if (sClipboardText.IsEmpty())
4067 return;
4069 sClipboardText.Replace(_T("\r\n"), _T("\r"));
4070 sClipboardText.Replace('\n', '\r');
4072 InsertText(sClipboardText);
4075 void CBaseView::OnCaretDown()
4077 POINT ptCaretPos = GetCaretPosition();
4078 int nLine = ptCaretPos.y;
4079 int nNextLine = nLine + 1;
4080 if (nNextLine >= GetLineCount()) // already at last line
4082 return;
4085 POINT ptCaretViewPos = GetCaretViewPosition();
4086 int nViewLine = ptCaretViewPos.y;
4087 int nNextViewLine = GetViewLineForScreen(nNextLine);
4088 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4090 // find next suitable screen line
4091 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4093 nNextLine++;
4094 if (nNextLine >= GetLineCount())
4096 return;
4098 nNextViewLine = GetViewLineForScreen(nNextLine);
4101 ptCaretPos.y = nNextLine;
4102 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4103 SetCaretPosition(ptCaretPos);
4104 OnCaretMove(MOVELEFT);
4105 ShowDiffLines(ptCaretPos.y);
4108 bool CBaseView::MoveCaretLeft()
4110 POINT ptCaretViewPos = GetCaretViewPosition();
4112 //int nViewLine = ptCaretViewPos.y;
4113 if (ptCaretViewPos.x == 0)
4115 int nPrevLine = GetCaretPosition().y;
4116 int nPrevViewLine;
4117 do {
4118 nPrevLine--;
4119 if (nPrevLine < 0)
4121 return false;
4123 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4124 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4125 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4126 ShowDiffLines(nPrevLine);
4128 else
4129 --ptCaretViewPos.x;
4131 SetCaretAndGoalViewPosition(ptCaretViewPos);
4132 return true;
4135 bool CBaseView::MoveCaretRight()
4137 POINT ptCaretViewPos = GetCaretViewPosition();
4139 int nViewLine = ptCaretViewPos.y;
4140 int nViewLineLen = GetViewLineLength(nViewLine);
4141 if (ptCaretViewPos.x >= nViewLineLen)
4143 int nNextLine = GetCaretPosition().y;
4144 int nNextViewLine;
4145 do {
4146 nNextLine++;
4147 if (nNextLine >= GetLineCount())
4149 return false;
4151 nNextViewLine = GetViewLineForScreen(nNextLine);
4152 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4153 ptCaretViewPos.y = nNextViewLine;
4154 ptCaretViewPos.x = 0;
4155 ShowDiffLines(nNextLine);
4157 else
4158 ++ptCaretViewPos.x;
4160 SetCaretAndGoalViewPosition(ptCaretViewPos);
4161 return true;
4164 void CBaseView::UpdateGoalPos()
4166 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4169 void CBaseView::OnCaretLeft()
4171 MoveCaretLeft();
4172 OnCaretMove(MOVELEFT);
4175 void CBaseView::OnCaretRight()
4177 MoveCaretRight();
4178 OnCaretMove(MOVERIGHT);
4181 void CBaseView::OnCaretUp()
4183 POINT ptCaretPos = GetCaretPosition();
4184 int nLine = ptCaretPos.y;
4185 if (nLine <= 0) // already at first line
4187 return;
4189 int nPrevLine = nLine - 1;
4191 POINT ptCaretViewPos = GetCaretViewPosition();
4192 int nViewLine = ptCaretViewPos.y;
4193 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4194 if (nPrevViewLine != nViewLine) // not on same view line
4196 // find previous suitable screen line
4197 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4199 if (nPrevLine <= 0)
4201 return;
4203 nPrevLine--;
4204 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4207 ptCaretPos.y = nPrevLine;
4208 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4209 SetCaretPosition(ptCaretPos);
4210 OnCaretMove(MOVELEFT);
4211 ShowDiffLines(ptCaretPos.y);
4214 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4216 switch (GetCharGroup(ch))
4218 case CHG_CONTROL:
4219 case CHG_WHITESPACE:
4220 case CHG_WORDSEPARATOR:
4221 return true;
4223 return false;
4226 bool CBaseView::IsCaretAtWordBoundary()
4228 POINT ptViewCaret = GetCaretViewPosition();
4229 CString line = GetViewLineChars(ptViewCaret.y);
4230 if (line.IsEmpty())
4231 return false; // no boundary at the empty lines
4232 if (ptViewCaret.x == 0)
4233 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4234 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4235 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4236 return
4237 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4238 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4241 void CBaseView::UpdateViewsCaretPosition()
4243 POINT ptCaretPos = GetCaretPosition();
4244 if (m_pwndBottom && m_pwndBottom!=this)
4245 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4246 if (m_pwndLeft && m_pwndLeft!=this)
4247 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4248 if (m_pwndRight && m_pwndRight!=this)
4249 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4252 void CBaseView::OnCaretWordleft()
4254 MoveCaretWordLeft();
4255 OnCaretMove(MOVELEFT);
4258 void CBaseView::OnCaretWordright()
4260 MoveCaretWordRight();
4261 OnCaretMove(MOVERIGHT);
4264 void CBaseView::MoveCaretWordLeft()
4266 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4271 void CBaseView::MoveCaretWordRight()
4273 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4278 void CBaseView::ClearCurrentSelection()
4280 m_ptSelectionViewPosStart = GetCaretViewPosition();
4281 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4282 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4283 m_nSelViewBlockStart = -1;
4284 m_nSelViewBlockEnd = -1;
4285 Invalidate(FALSE);
4288 void CBaseView::ClearSelection()
4290 if (m_pwndLeft)
4291 m_pwndLeft->ClearCurrentSelection();
4292 if (m_pwndRight)
4293 m_pwndRight->ClearCurrentSelection();
4294 if (m_pwndBottom)
4295 m_pwndBottom->ClearCurrentSelection();
4298 void CBaseView::AdjustSelection(bool bMoveLeft)
4300 POINT ptCaretViewPos = GetCaretViewPosition();
4301 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4303 // select all have been used recently update origin
4304 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4306 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4307 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4309 m_ptSelectionViewPosStart = ptCaretViewPos;
4310 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4312 else
4314 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4315 m_ptSelectionViewPosEnd = ptCaretViewPos;
4318 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4320 Invalidate(FALSE);
4323 void CBaseView::OnEditCut()
4325 if (IsWritable())
4327 OnEditCopy();
4328 RemoveSelectedText();
4332 void CBaseView::OnEditPaste()
4334 if (IsWritable())
4336 CUndo::GetInstance().BeginGrouping();
4337 RemoveSelectedText();
4338 PasteText();
4339 CUndo::GetInstance().EndGrouping();
4343 void CBaseView::DeleteFonts()
4345 for (int i=0; i<fontsCount; i++)
4347 if (m_apFonts[i] != NULL)
4349 m_apFonts[i]->DeleteObject();
4350 delete m_apFonts[i];
4351 m_apFonts[i] = NULL;
4356 void CBaseView::OnCaretMove(bool bMoveLeft)
4358 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4359 OnCaretMove(bMoveLeft, bShift);
4362 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4364 if(isShiftPressed)
4365 AdjustSelection(bMoveLeft);
4366 else
4367 ClearSelection();
4368 EnsureCaretVisible();
4369 UpdateCaret();
4372 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4374 AddCutCopyAndPaste(popup);
4377 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4379 popup.AppendMenu(MF_SEPARATOR, NULL);
4380 CString temp;
4381 temp.LoadString(IDS_EDIT_COPY);
4382 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4383 if (IsWritable())
4385 temp.LoadString(IDS_EDIT_CUT);
4386 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4387 temp.LoadString(IDS_EDIT_PASTE);
4388 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4389 popup.AppendMenu(MF_SEPARATOR, NULL);
4393 void CBaseView::CompensateForKeyboard(CPoint& point)
4395 // if the context menu is invoked through the keyboard, we have to use
4396 // a calculated position on where to anchor the menu on
4397 if (ArePointsSame(point, SetupPoint(-1, -1)))
4399 CRect rect;
4400 GetWindowRect(&rect);
4401 point = rect.CenterPoint();
4405 HICON CBaseView::LoadIcon(WORD iconId)
4407 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4408 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4409 return (HICON)icon;
4412 void CBaseView::ReleaseBitmap()
4414 if (m_pCacheBitmap != NULL)
4416 m_pCacheBitmap->DeleteObject();
4417 delete m_pCacheBitmap;
4418 m_pCacheBitmap = NULL;
4422 void CBaseView::BuildMarkedWordArray()
4424 int lineCount = GetLineCount();
4425 m_arMarkedWordLines.clear();
4426 m_arMarkedWordLines.reserve(lineCount);
4427 bool bDoit = !m_sMarkedWord.IsEmpty();
4428 for (int i = 0; i < lineCount; ++i)
4430 if (bDoit)
4432 CString line = GetLineChars(i);
4434 if (!line.IsEmpty())
4436 int found = 0;
4437 int nMarkStart = -1;
4438 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4440 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4441 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4442 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4443 if (eLeft != eStart)
4445 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4446 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4447 if (eRight != eEnd)
4449 found = 1;
4450 break;
4454 m_arMarkedWordLines.push_back(found);
4456 else
4457 m_arMarkedWordLines.push_back(0);
4459 else
4460 m_arMarkedWordLines.push_back(0);
4464 void CBaseView::BuildFindStringArray()
4466 int lineCount = GetLineCount();
4467 m_arFindStringLines.clear();
4468 m_arFindStringLines.reserve(lineCount);
4469 bool bDoit = !m_sFindText.IsEmpty();
4470 int s = 0;
4471 int e = 0;
4472 for (int i = 0; i < lineCount; ++i)
4474 if (bDoit)
4476 CString line = GetLineChars(i);
4478 if (!line.IsEmpty())
4480 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4482 case DIFFSTATE_EMPTY:
4483 m_arFindStringLines.push_back(0);
4484 break;
4485 case DIFFSTATE_UNKNOWN:
4486 case DIFFSTATE_NORMAL:
4487 if (m_bLimitToDiff)
4489 m_arFindStringLines.push_back(0);
4490 break;
4492 case DIFFSTATE_REMOVED:
4493 case DIFFSTATE_REMOVEDWHITESPACE:
4494 case DIFFSTATE_ADDED:
4495 case DIFFSTATE_ADDEDWHITESPACE:
4496 case DIFFSTATE_WHITESPACE:
4497 case DIFFSTATE_WHITESPACE_DIFF:
4498 case DIFFSTATE_CONFLICTED:
4499 case DIFFSTATE_CONFLICTED_IGNORED:
4500 case DIFFSTATE_CONFLICTADDED:
4501 case DIFFSTATE_CONFLICTEMPTY:
4502 case DIFFSTATE_CONFLICTRESOLVED:
4503 case DIFFSTATE_IDENTICALREMOVED:
4504 case DIFFSTATE_IDENTICALADDED:
4505 case DIFFSTATE_THEIRSREMOVED:
4506 case DIFFSTATE_THEIRSADDED:
4507 case DIFFSTATE_YOURSREMOVED:
4508 case DIFFSTATE_YOURSADDED:
4509 case DIFFSTATE_EDITED:
4511 if (!m_bMatchCase)
4512 line = line.MakeLower();
4513 s = 0;
4514 e = 0;
4515 int match = 0;
4516 while (StringFound(line, SearchNext, s, e))
4518 match++;
4519 s = e;
4520 e = 0;
4522 m_arFindStringLines.push_back(match);
4523 break;
4525 default:
4526 m_arFindStringLines.push_back(0);
4529 else
4530 m_arFindStringLines.push_back(0);
4532 else
4533 m_arFindStringLines.push_back(0);
4535 UpdateLocator();
4538 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4540 if (!m_bShowInlineDiff)
4541 return false;
4542 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4543 return false;
4545 if (m_pViewData == nullptr || m_pViewData->GetCount() <= nViewLine)
4546 return false;
4547 const CString &sLine = m_pViewData->GetLine(nViewLine);
4548 if (sLine.IsEmpty())
4549 return false;
4551 CheckOtherView();
4552 if (!m_pOtherViewData)
4553 return false;
4555 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4556 if (sDiffLine.IsEmpty())
4557 return false;
4559 svn_diff_t * diff = NULL;
4560 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4561 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4562 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4563 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4564 return false;
4566 size_t lineoffset = 0;
4567 size_t position = 0;
4568 while (diff)
4570 if (this == m_pwndRight)
4572 apr_off_t nTmp = diff->modified_length;
4573 diff->modified_length = diff->original_length;
4574 diff->original_length = nTmp;
4576 nTmp = diff->modified_start;
4577 diff->modified_start = diff->original_start;
4578 diff->original_start = nTmp;
4580 apr_off_t len = diff->original_length;
4581 size_t oldpos = position;
4583 for (apr_off_t i = 0; i < len; ++i)
4585 position += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4586 lineoffset++;
4589 if (diff->type == svn_diff__type_diff_modified)
4591 inlineDiffPos p;
4592 p.start = oldpos;
4593 p.end = position;
4594 positions.push_back(p);
4597 diff = diff->next;
4600 return !positions.empty();
4603 void CBaseView::OnNavigateNextinlinediff()
4605 int nX;
4606 if (GetNextInlineDiff(nX))
4608 POINT ptCaretViewPos = GetCaretViewPosition();
4609 ptCaretViewPos.x = nX;
4610 SetCaretAndGoalViewPosition(ptCaretViewPos);
4611 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4612 EnsureCaretVisible();
4616 void CBaseView::OnNavigatePrevinlinediff()
4618 int nX;
4619 if (GetPrevInlineDiff(nX))
4621 POINT ptCaretViewPos = GetCaretViewPosition();
4622 ptCaretViewPos.x = nX;
4623 SetCaretAndGoalViewPosition(ptCaretViewPos);
4624 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4625 EnsureCaretVisible();
4629 bool CBaseView::HasNextInlineDiff()
4631 int nPos;
4632 return GetNextInlineDiff(nPos);
4635 bool CBaseView::GetNextInlineDiff(int & nPos)
4637 POINT ptCaretViewPos = GetCaretViewPosition();
4638 std::vector<inlineDiffPos> positions;
4639 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4641 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4643 if (it->start > ptCaretViewPos.x)
4645 nPos = (LONG)it->start;
4646 return true;
4648 if (it->end > ptCaretViewPos.x)
4650 nPos = (LONG)it->end;
4651 return true;
4655 return false;
4658 bool CBaseView::HasPrevInlineDiff()
4660 int nPos;
4661 return GetPrevInlineDiff(nPos);
4664 bool CBaseView::GetPrevInlineDiff(int & nPos)
4666 POINT ptCaretViewPos = GetCaretViewPosition();
4667 std::vector<inlineDiffPos> positions;
4668 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4670 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4672 if ( it->end < ptCaretViewPos.x)
4674 nPos = (LONG)it->end;
4675 return true;
4677 if ( it->start < ptCaretViewPos.x)
4679 nPos = (LONG)it->start;
4680 return true;
4684 return false;
4687 CBaseView * CBaseView::GetFirstGoodView()
4689 if (IsViewGood(m_pwndLeft))
4690 return m_pwndLeft;
4691 if (IsViewGood(m_pwndRight))
4692 return m_pwndRight;
4693 if (IsViewGood(m_pwndBottom))
4694 return m_pwndBottom;
4695 return NULL;
4698 void CBaseView::BuildAllScreen2ViewVector()
4700 CBaseView * p_pwndView = GetFirstGoodView();
4701 if (p_pwndView)
4703 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4707 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4709 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4712 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4714 CBaseView * p_pwndView = GetFirstGoodView();
4715 if (p_pwndView)
4717 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4721 void CBaseView::UpdateViewLineNumbers()
4723 int nLineNumber = 0;
4724 int nViewLineCount = GetViewCount();
4725 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4727 int oldLine = (int)GetViewLineNumber(nViewLine);
4728 if (oldLine >= 0)
4729 SetViewLineNumber(nViewLine, nLineNumber++);
4731 m_nDigits = 0;
4734 int CBaseView::CleanEmptyLines()
4736 int nRemovedCount = 0;
4737 int nViewLineCount = GetViewCount();
4738 bool bCheckLeft = IsViewGood(m_pwndLeft);
4739 bool bCheckRight = IsViewGood(m_pwndRight);
4740 bool bCheckBottom = IsViewGood(m_pwndBottom);
4741 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4743 bool bAllEmpty = true;
4744 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4745 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4746 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4747 if (bAllEmpty)
4749 if (bCheckLeft)
4751 m_pwndLeft->RemoveViewData(nViewLine);
4753 if (bCheckRight)
4755 m_pwndRight->RemoveViewData(nViewLine);
4757 if (bCheckBottom)
4759 m_pwndBottom->RemoveViewData(nViewLine);
4761 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4763 SaveUndoStep();
4765 nViewLineCount--;
4766 nRemovedCount++;
4767 continue;
4769 nViewLine++;
4771 return nRemovedCount;
4774 int CBaseView::FindScreenLineForViewLine( int viewLine )
4776 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4779 int CBaseView::CountMultiLines( int nViewLine )
4781 if (m_ScreenedViewLine.empty())
4782 return 0; // in case the view is completely empty
4784 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4786 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4788 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4791 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4793 TScreenedViewLine oScreenedLine;
4794 // tokenize string
4795 int prevpos = 0;
4796 int pos = 0;
4797 while ((pos = multiline.Find('\n', pos)) >= 0)
4799 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4800 pos++;
4801 prevpos = pos;
4803 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4804 oScreenedLine.bSublinesSet = true;
4805 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4807 return CountMultiLines(nViewLine);
4810 /// prepare inline diff cache
4811 LineColors & CBaseView::GetLineColors(int nViewLine)
4813 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4815 if (m_bWhitespaceInlineDiffs)
4817 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4818 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4820 else
4822 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4823 return m_ScreenedViewLine[nViewLine].lineColors;
4826 LineColors oLineColors;
4827 // set main line color
4828 COLORREF crBkgnd, crText;
4829 DiffStates diffState = m_pViewData->GetState(nViewLine);
4830 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4831 oLineColors.SetColor(0, crText, crBkgnd);
4833 do {
4834 if (!m_bShowInlineDiff)
4835 break;
4837 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4838 break;
4840 CString sLine = GetViewLineChars(nViewLine);
4841 if (sLine.IsEmpty())
4842 break;
4843 if (!m_pOtherView)
4844 break;
4846 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4847 if (sDiffLine.IsEmpty())
4848 break;
4850 svn_diff_t * diff = NULL;
4851 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4852 break;
4853 auto pLine1 = (this == m_pwndLeft) ? &sLine : &sDiffLine;
4854 auto pLine2 = (this == m_pwndLeft) ? &sDiffLine : &sLine;
4855 m_svnlinediff.Diff(&diff, *pLine1, pLine1->GetLength(), *pLine2, pLine2->GetLength(), m_bInlineWordDiff);
4856 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4857 break;
4859 int lineoffset = 0;
4860 int nTextStartOffset = 0;
4861 std::map<int, COLORREF> removedPositions;
4862 while (diff)
4864 if (this == m_pwndRight)
4866 apr_off_t nTmp = diff->modified_length;
4867 diff->modified_length = diff->original_length;
4868 diff->original_length = nTmp;
4870 nTmp = diff->modified_start;
4871 diff->modified_start = diff->original_start;
4872 diff->original_start = nTmp;
4874 apr_off_t len = diff->original_length;
4876 size_t nTextLength = 0;
4877 for (int i = 0; i < len; ++i)
4879 nTextLength += (this == m_pwndRight) ? m_svnlinediff.m_line2tokens[lineoffset].size() : m_svnlinediff.m_line1tokens[lineoffset].size();
4880 lineoffset++;
4882 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4884 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4885 if ((m_bShowInlineDiff)&&(bInlineDiff))
4887 crBkgnd = InlineViewLineDiffColor(nViewLine);
4889 else
4891 crBkgnd = m_ModifiedBk;
4894 if (len < diff->modified_length)
4896 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4898 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4900 nTextStartOffset += (int)nTextLength;
4901 diff = diff->next;
4903 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4905 oLineColors.AddShotColor(it->first, it->second);
4907 } while (false); // error catch
4909 if (!m_bWhitespaceInlineDiffs)
4911 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4912 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4914 else
4916 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4917 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4920 return GetLineColors(nViewLine);
4923 void CBaseView::OnEditSelectall()
4925 if (m_pViewData == nullptr)
4926 return;
4927 int nLastViewLine = m_pViewData->GetCount()-1;
4928 if (nLastViewLine < 0)
4929 return;
4930 SetupAllViewSelection(0, nLastViewLine);
4932 CString sLine = GetViewLineChars(nLastViewLine);
4933 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4934 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4935 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4937 UpdateWindow();
4940 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4942 FilterWhitespaces(first);
4943 FilterWhitespaces(second);
4946 void CBaseView::FilterWhitespaces(CString& line)
4948 line.Remove(' ');
4949 line.Remove('\t');
4950 line.Remove('\r');
4951 line.Remove('\n');
4954 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4956 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4957 int nEventLine = nLineFromTop + m_nTopLine;
4958 nEventLine--; //we need the index
4959 return nEventLine;
4963 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4965 if (RelayTrippleClick(pMsg))
4966 return TRUE;
4967 return CView::PreTranslateMessage(pMsg);
4971 void CBaseView::ResetUndoStep()
4973 m_AllState.Clear();
4976 void CBaseView::SaveUndoStep()
4978 if (!m_AllState.IsEmpty())
4980 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4982 ResetUndoStep();
4985 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4987 m_pState->addedlines.push_back(index);
4988 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4991 void CBaseView::InsertViewData( int index, const viewdata& data )
4993 m_pState->addedlines.push_back(index);
4994 m_pViewData->InsertData(index, data);
4997 void CBaseView::RemoveViewData( int index )
4999 m_pState->removedlines[index] = m_pViewData->GetData(index);
5000 m_pViewData->RemoveData(index);
5003 void CBaseView::SetViewData( int index, const viewdata& data )
5005 m_pState->replacedlines[index] = m_pViewData->GetData(index);
5006 m_pViewData->SetData(index, data);
5009 void CBaseView::SetViewState( int index, DiffStates state )
5011 m_pState->linestates[index] = m_pViewData->GetState(index);
5012 m_pViewData->SetState(index, state);
5015 void CBaseView::SetViewLine( int index, const CString& sLine )
5017 m_pState->difflines[index] = m_pViewData->GetLine(index);
5018 m_pViewData->SetLine(index, sLine);
5021 void CBaseView::SetViewLineNumber( int index, int linenumber )
5023 int oldLineNumber = m_pViewData->GetLineNumber(index);
5024 if (oldLineNumber != linenumber) {
5025 m_pState->linelines[index] = oldLineNumber;
5026 m_pViewData->SetLineNumber(index, linenumber);
5030 void CBaseView::SetViewLineEnding( int index, EOL ending )
5032 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
5033 m_pViewData->SetLineEnding(index, ending);
5036 void CBaseView::SetViewMarked( int index, bool marked )
5038 m_pState->markedlines[index] = m_pViewData->GetMarked(index);
5039 m_pViewData->SetMarked(index, marked);
5043 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
5045 if (HasSelection())
5047 start = m_nSelViewBlockStart;
5048 end = m_nSelViewBlockEnd;
5049 return true;
5051 return false;
5054 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
5056 RebuildIfNecessary();
5057 if ((size() <= screenLine) || (screenLine < 0))
5058 return 0;
5059 return m_Screen2View[screenLine].nViewLine;
5062 int CBaseView::Screen2View::size()
5064 RebuildIfNecessary();
5065 return (int)m_Screen2View.size();
5068 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5070 RebuildIfNecessary();
5071 if (size() <= screenLine)
5072 return 0;
5073 return m_Screen2View[screenLine].nViewSubLine;
5076 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5078 RebuildIfNecessary();
5079 return m_Screen2View[screenLine];
5083 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5085 void CBaseView::Screen2View::RebuildIfNecessary()
5087 if (!m_pViewData)
5088 return; // rebuild not necessary
5090 FixScreenedCacheSize(m_pwndLeft);
5091 FixScreenedCacheSize(m_pwndRight);
5092 FixScreenedCacheSize(m_pwndBottom);
5093 if (!m_bFull)
5095 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5097 ResetScreenedViewLineCache(m_pwndLeft, *it);
5098 ResetScreenedViewLineCache(m_pwndRight, *it);
5099 ResetScreenedViewLineCache(m_pwndBottom, *it);
5102 else
5104 ResetScreenedViewLineCache(m_pwndLeft);
5105 ResetScreenedViewLineCache(m_pwndRight);
5106 ResetScreenedViewLineCache(m_pwndBottom);
5108 m_RebuildRanges.clear();
5109 m_bFull = false;
5111 size_t OldSize = m_Screen2View.size();
5112 m_Screen2View.clear();
5113 m_Screen2View.reserve(OldSize); // guess same size
5114 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5116 if (m_pMainFrame->m_bCollapsed)
5118 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5119 ++i;
5120 if (!(i < m_pViewData->GetCount()))
5121 break;
5123 TScreenLineInfo oLineInfo;
5124 oLineInfo.nViewLine = i;
5125 oLineInfo.nViewSubLine = -1; // no wrap
5126 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5128 int nMaxLines = 0;
5129 if (IsLeftViewGood())
5130 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5131 if (IsRightViewGood())
5132 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5133 if (IsBottomViewGood())
5134 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5135 for (int l = 0; l < (nMaxLines-1); ++l)
5137 oLineInfo.nViewSubLine++;
5138 m_Screen2View.push_back(oLineInfo);
5140 oLineInfo.nViewSubLine++;
5142 m_Screen2View.push_back(oLineInfo);
5144 m_pViewData = NULL;
5146 if (IsLeftViewGood())
5147 m_pwndLeft->BuildMarkedWordArray();
5148 if (IsRightViewGood())
5149 m_pwndRight->BuildMarkedWordArray();
5150 if (IsBottomViewGood())
5151 m_pwndBottom->BuildMarkedWordArray();
5152 UpdateLocator();
5153 RecalcAllVertScrollBars();
5154 RecalcAllHorzScrollBars();
5157 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5159 RebuildIfNecessary();
5161 int nScreenLineCount = (int)m_Screen2View.size();
5163 int nPos = 0;
5164 if (nScreenLineCount>16)
5166 // for enough long data search for last screen
5167 // with viewline less than one we are looking for
5168 // use approximate method (based on) binary search using asymmetric start point
5169 // in form 2**n (determined as MSB of length) to go around division and rounding;
5170 // this effectively looks for bit values from MSB to LSB
5172 int nTestBit;
5173 //GetMostSignificantBitValue
5174 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5175 nTestBit = nScreenLineCount;
5176 nTestBit |= nTestBit>>1;
5177 nTestBit |= nTestBit>>2;
5178 nTestBit |= nTestBit>>4;
5179 nTestBit |= nTestBit>>8;
5180 nTestBit |= nTestBit>>16;
5181 nTestBit ^= (nTestBit>>1);
5183 while (nTestBit)
5185 int nTestPos = nPos | nTestBit;
5186 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5188 nPos = nTestPos;
5190 nTestBit >>= 1;
5193 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5195 nPos++;
5198 return nPos;
5201 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5202 m_bFull = true;
5204 m_pViewData = pViewData;
5207 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5209 if (m_bFull)
5210 return;
5212 m_pViewData = pViewData;
5214 TRebuildRange Range;
5215 Range.FirstViewLine=nFirstViewLine;
5216 Range.LastViewLine=nLastViewLine;
5217 m_RebuildRanges.push_back(Range);
5220 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5222 if (!IsViewGood(pwndView))
5224 return false;
5226 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5227 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5228 if (nOldSize == nViewCount)
5230 return false;
5232 pwndView->m_ScreenedViewLine.resize(nViewCount);
5233 return true;
5236 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5238 if (!IsViewGood(pwndView))
5240 return false;
5242 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5243 ResetScreenedViewLineCache(pwndView, Range);
5244 return true;
5247 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5249 if (!IsViewGood(pwndView))
5251 return false;
5253 if (Range.LastViewLine == -1)
5255 return false;
5257 ASSERT(Range.FirstViewLine >= 0);
5258 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5259 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5261 pwndView->m_ScreenedViewLine[i].Clear();
5263 return false;
5266 void CBaseView::WrapChanged()
5268 m_nMaxLineLength = -1;
5269 m_nOffsetChar = 0;
5272 void CBaseView::OnEditFind()
5274 if (m_pFindDialog)
5275 return;
5277 m_pFindDialog = new CFindDlg(this);
5278 m_pFindDialog->Create(this);
5280 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5281 m_pFindDialog->SetReadonly(m_bReadonly);
5284 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5286 ASSERT(m_pFindDialog != NULL);
5288 if (m_pFindDialog->IsTerminating())
5290 // invalidate the handle identifying the dialog box.
5291 m_pFindDialog = NULL;
5292 return 0;
5295 if(m_pFindDialog->FindNext())
5297 //read data from dialog
5298 m_sFindText = m_pFindDialog->GetFindString();
5299 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5300 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5301 m_bWholeWord = m_pFindDialog->WholeWord();
5303 if (!m_bMatchCase)
5304 m_sFindText = m_sFindText.MakeLower();
5306 BuildFindStringArray();
5307 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5309 if (m_pFindDialog->SearchUp())
5310 OnEditFindprev();
5311 else
5312 OnEditFindnext();
5314 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5316 size_t count = 0;
5317 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5318 count += m_arFindStringLines[i];
5319 CString format;
5320 format.LoadString(IDS_FIND_COUNT);
5321 CString matches;
5322 matches.Format(format, count);
5323 m_pFindDialog->SetStatusText(matches);
5325 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5327 if (!IsWritable())
5328 return 0;
5329 bool bFound = false;
5330 if (m_pFindDialog->SearchUp())
5331 bFound = Search(SearchPrevious, true, true, false);
5332 else
5333 bFound = Search(SearchNext, true, true, false);
5334 if (bFound)
5336 CString sReplaceText = m_pFindDialog->GetReplaceString();
5337 CUndo::GetInstance().BeginGrouping();
5338 RemoveSelectedText();
5339 InsertText(sReplaceText);
5340 CUndo::GetInstance().EndGrouping();
5344 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5346 if (!IsWritable())
5347 return 0;
5348 bool bFound = false;
5349 int replaceCount = 0;
5350 POINT lastPoint = m_ptSelectionViewPosStart;
5351 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5352 CUndo::GetInstance().BeginGrouping();
5355 bFound = Search(SearchNext, true, false, true);
5356 if (bFound)
5358 CString sReplaceText = m_pFindDialog->GetReplaceString();
5359 RemoveSelectedText();
5360 InsertText(sReplaceText);
5361 ++replaceCount;
5363 } while (bFound);
5364 CUndo::GetInstance().EndGrouping();
5365 if (replaceCount == 0)
5366 m_ptSelectionViewPosStart = lastPoint;
5367 CString message;
5368 message.Format(IDS_FIND_REPLACED, replaceCount);
5369 if (m_pFindDialog)
5370 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5375 return 0;
5378 void CBaseView::OnEditFindnextStart()
5380 if (m_pViewData == nullptr)
5381 return;
5382 if (HasTextSelection())
5384 m_sFindText = GetSelectedText();
5385 m_bMatchCase = false;
5386 m_bLimitToDiff = false;
5387 m_bWholeWord = false;
5388 m_sFindText = m_sFindText.MakeLower();
5390 BuildFindStringArray();
5391 OnEditFindnext();
5393 else
5395 m_sFindText.Empty();
5396 BuildFindStringArray();
5400 void CBaseView::OnEditFindprevStart()
5402 if (m_pViewData == nullptr)
5403 return;
5404 if (HasTextSelection())
5406 m_sFindText = GetSelectedText();
5407 m_bMatchCase = false;
5408 m_bLimitToDiff = false;
5409 m_bWholeWord = false;
5410 m_sFindText = m_sFindText.MakeLower();
5412 BuildFindStringArray();
5413 OnEditFindprev();
5415 else
5417 m_sFindText.Empty();
5418 BuildFindStringArray();
5422 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5424 if (srchDir == SearchPrevious)
5426 int laststart = -1;
5427 int laststart2 = -1;
5430 laststart2 = laststart;
5431 laststart = str.Find(m_sFindText, laststart + 1);
5432 } while (laststart >= 0 && laststart < start);
5433 start = laststart2;
5435 else
5436 start = str.Find(m_sFindText, start);
5437 end = start + m_sFindText.GetLength();
5438 bool bStringFound = (start >= 0);
5439 if (bStringFound && m_bWholeWord)
5441 if (start)
5442 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5444 if (bStringFound)
5446 if (str.GetLength() > end)
5447 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5450 return bStringFound;
5453 void CBaseView::OnEditFindprev()
5455 Search(SearchPrevious, false, true, false);
5458 void CBaseView::OnEditFindnext()
5460 Search(SearchNext, false, true, false);
5463 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5465 if (m_sFindText.IsEmpty())
5466 return false;
5467 if(!m_pViewData)
5468 return false;
5470 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5471 POINT end;
5472 end.y = m_pViewData->GetCount()-1;
5473 if (end.y < 0)
5474 return false;
5476 if (srchDir==SearchNext)
5477 end.x = GetViewLineLength(end.y);
5478 else
5480 end.x = m_ptSelectionViewPosStart.x;
5481 start.x = 0;
5484 if (!HasTextSelection())
5486 start.y = m_ptCaretViewPos.y;
5487 if (srchDir==SearchNext)
5488 start.x = m_ptCaretViewPos.x;
5489 else
5491 start.x = 0;
5492 end.x = m_ptCaretViewPos.x;
5495 CString sSelectedText;
5496 int startline = -1;
5497 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5499 if (nViewLine < 0)
5501 if (stopEof)
5502 return false;
5503 nViewLine = m_pViewData->GetCount()-1;
5504 startline = start.y;
5505 if (flashIfNotFound)
5507 if (m_pFindDialog)
5508 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5509 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5512 if (nViewLine > end.y)
5514 if (stopEof)
5515 return false;
5516 nViewLine = 0;
5517 startline = start.y;
5518 if (flashIfNotFound)
5520 if (m_pFindDialog)
5521 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5522 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5525 switch (m_pViewData->GetState(nViewLine))
5527 case DIFFSTATE_EMPTY:
5528 break;
5529 case DIFFSTATE_UNKNOWN:
5530 case DIFFSTATE_NORMAL:
5531 if (m_bLimitToDiff)
5532 break;
5533 case DIFFSTATE_REMOVED:
5534 case DIFFSTATE_REMOVEDWHITESPACE:
5535 case DIFFSTATE_ADDED:
5536 case DIFFSTATE_ADDEDWHITESPACE:
5537 case DIFFSTATE_WHITESPACE:
5538 case DIFFSTATE_WHITESPACE_DIFF:
5539 case DIFFSTATE_CONFLICTED:
5540 case DIFFSTATE_CONFLICTED_IGNORED:
5541 case DIFFSTATE_CONFLICTADDED:
5542 case DIFFSTATE_CONFLICTEMPTY:
5543 case DIFFSTATE_CONFLICTRESOLVED:
5544 case DIFFSTATE_IDENTICALREMOVED:
5545 case DIFFSTATE_IDENTICALADDED:
5546 case DIFFSTATE_THEIRSREMOVED:
5547 case DIFFSTATE_THEIRSADDED:
5548 case DIFFSTATE_YOURSREMOVED:
5549 case DIFFSTATE_YOURSADDED:
5550 case DIFFSTATE_EDITED:
5552 sSelectedText = GetViewLineChars(nViewLine);
5553 if (nViewLine == start.y && startline < 0)
5554 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5555 if (!m_bMatchCase)
5556 sSelectedText = sSelectedText.MakeLower();
5557 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5558 int endfound = 0;
5559 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5561 HighlightViewLines(nViewLine, nViewLine);
5562 m_ptSelectionViewPosStart.x = startfound;
5563 m_ptSelectionViewPosEnd.x = endfound;
5564 if (nViewLine == start.y && startline < 0)
5566 m_ptSelectionViewPosStart.x += start.x;
5567 m_ptSelectionViewPosEnd.x += start.x;
5569 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5570 m_ptSelectionViewPosStart.y = nViewLine;
5571 m_ptSelectionViewPosEnd.y = nViewLine;
5572 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5573 UpdateViewsCaretPosition();
5574 EnsureCaretVisible();
5575 Invalidate();
5576 return true;
5579 break;
5582 if (startline >= 0)
5584 if (nViewLine == startline)
5586 if (flashIfNotFound)
5588 CString message;
5589 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5590 if (m_pFindDialog)
5591 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5592 ::MessageBeep(0xFFFFFFFF);
5593 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5595 break;
5599 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5600 return false;
5603 CString CBaseView::GetSelectedText() const
5605 CString sSelectedText;
5606 POINT start = m_ptSelectionViewPosStart;
5607 POINT end = m_ptSelectionViewPosEnd;
5608 if (!HasTextSelection())
5610 if (!HasSelection())
5611 return sSelectedText;
5612 start.y = m_nSelViewBlockStart;
5613 start.x = 0;
5614 end.y = m_nSelViewBlockEnd;
5615 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5617 if (m_pViewData == nullptr)
5618 return sSelectedText;
5619 // first store the selected lines in one CString
5620 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5622 switch (m_pViewData->GetState(nViewLine))
5624 case DIFFSTATE_EMPTY:
5625 break;
5626 case DIFFSTATE_UNKNOWN:
5627 case DIFFSTATE_NORMAL:
5628 case DIFFSTATE_REMOVED:
5629 case DIFFSTATE_REMOVEDWHITESPACE:
5630 case DIFFSTATE_ADDED:
5631 case DIFFSTATE_ADDEDWHITESPACE:
5632 case DIFFSTATE_WHITESPACE:
5633 case DIFFSTATE_WHITESPACE_DIFF:
5634 case DIFFSTATE_CONFLICTED:
5635 case DIFFSTATE_CONFLICTED_IGNORED:
5636 case DIFFSTATE_CONFLICTADDED:
5637 case DIFFSTATE_CONFLICTEMPTY:
5638 case DIFFSTATE_CONFLICTRESOLVED:
5639 case DIFFSTATE_IDENTICALREMOVED:
5640 case DIFFSTATE_IDENTICALADDED:
5641 case DIFFSTATE_THEIRSREMOVED:
5642 case DIFFSTATE_THEIRSADDED:
5643 case DIFFSTATE_YOURSREMOVED:
5644 case DIFFSTATE_YOURSADDED:
5645 case DIFFSTATE_EDITED:
5646 sSelectedText += GetViewLineChars(nViewLine);
5647 sSelectedText += _T("\r\n");
5648 break;
5651 // remove the non-selected chars from the first line, last line and last \r\n
5652 int nLeftCut = start.x;
5653 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5654 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5655 return sSelectedText;
5658 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5660 hasMods = false;
5661 hasConflicts = false;
5662 hasWhitespaceMods = false;
5664 if (m_pViewData)
5666 for (int i=0; i<m_pViewData->GetCount(); i++)
5668 DiffStates state = m_pViewData->GetState(i);
5669 switch (state)
5671 case DIFFSTATE_ADDED:
5672 case DIFFSTATE_IDENTICALADDED:
5673 case DIFFSTATE_THEIRSADDED:
5674 case DIFFSTATE_YOURSADDED:
5675 case DIFFSTATE_CONFLICTADDED:
5676 case DIFFSTATE_IDENTICALREMOVED:
5677 case DIFFSTATE_REMOVED:
5678 case DIFFSTATE_THEIRSREMOVED:
5679 case DIFFSTATE_YOURSREMOVED:
5680 case DIFFSTATE_EMPTY:
5681 hasMods = true;
5682 break;
5683 case DIFFSTATE_CONFLICTED:
5684 case DIFFSTATE_CONFLICTED_IGNORED:
5685 hasConflicts = true;
5686 break;
5687 case DIFFSTATE_REMOVEDWHITESPACE:
5688 case DIFFSTATE_ADDEDWHITESPACE:
5689 case DIFFSTATE_WHITESPACE:
5690 case DIFFSTATE_WHITESPACE_DIFF:
5691 hasWhitespaceMods = true;
5692 break;
5698 void CBaseView::OnEditGotoline()
5700 if (m_pViewData == NULL)
5701 return;
5702 // find the last and first line number
5703 int nViewLineCount = m_pViewData->GetCount();
5705 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5706 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5708 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5709 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5711 break;
5714 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5716 return;
5718 nLastLineNumber++;
5719 int nFirstLineNumber=1; // first is always 1
5721 CString sText;
5722 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5724 CGotoLineDlg dlg(this);
5725 dlg.SetLabel(sText);
5726 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5727 if (dlg.DoModal() == IDOK)
5729 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5731 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5733 HighlightViewLines(nViewLine, nViewLine);
5734 return;
5740 void CBaseView::OnToggleReadonly()
5742 if (IsReadonlyChangable()) {
5743 SetWritable(IsReadonly());
5747 int CBaseView::SaveFile(int nFlags)
5749 Invalidate();
5750 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5752 CFileTextLines file;
5753 m_SaveParams.m_LineEndings = m_lineendings;
5754 m_SaveParams.m_UnicodeType = m_texttype;
5755 file.SetSaveParams(m_SaveParams);
5757 for (int i=0; i<m_pViewData->GetCount(); i++)
5759 //only copy non-removed lines
5760 DiffStates state = m_pViewData->GetState(i);
5761 switch (state)
5763 case DIFFSTATE_CONFLICTED:
5764 case DIFFSTATE_CONFLICTED_IGNORED:
5766 int first = i;
5767 int last = i;
5770 last++;
5771 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5772 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5773 for (int j=first; j<last; j++)
5775 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5777 file.Add(_T("======="), EOL_NOENDING);
5778 for (int j=first; j<last; j++)
5780 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5782 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5783 i = last-1;
5785 break;
5786 case DIFFSTATE_EMPTY:
5787 break;
5788 case DIFFSTATE_CONFLICTEMPTY:
5789 case DIFFSTATE_IDENTICALREMOVED:
5790 case DIFFSTATE_REMOVED:
5791 case DIFFSTATE_THEIRSREMOVED:
5792 case DIFFSTATE_YOURSREMOVED:
5793 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5794 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5796 // do not save removed lines
5797 break;
5799 default:
5800 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5801 break;
5804 CString filename = m_pWorkingFile->GetFilename();
5805 if (m_pWorkingFile->IsReadonly())
5806 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5807 return -1;
5808 if (!file.Save(filename))
5810 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5811 return -1;
5813 m_pWorkingFile->SetFileName(filename);
5814 m_pWorkingFile->StoreFileAttributes();
5815 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5816 SetModified(FALSE);
5817 CUndo::GetInstance().MarkAsOriginalState(
5818 this == m_pwndLeft,
5819 this == m_pwndRight,
5820 this == m_pwndBottom);
5821 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5822 return 0;
5823 return file.GetCount();
5825 return 1;
5829 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5831 if (m_pWorkingFile)
5833 m_pWorkingFile->SetFileName(sFileName);
5834 return SaveFile(nFlags);
5836 return -1;
5840 EOL CBaseView::GetLineEndings()
5842 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5845 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5847 if (bHasMixedEols)
5849 return EOL_AUTOLINE; // mixed eols - hack value
5851 if (m_lineendings == EOL_AUTOLINE)
5853 return EOL_CRLF;
5855 return m_lineendings;
5858 void CBaseView::ReplaceLineEndings(EOL eEol)
5860 if (eEol == EOL_AUTOLINE)
5862 return;
5864 // set AUTOLINE
5865 m_lineendings = eEol;
5866 // replace all set EOLs
5867 // TODO store line endings and lineendings in undo
5868 //CUndo::BeginGrouping();
5869 for (int i = 0; i < GetViewCount(); ++i)
5871 if (IsLineEmpty(i))
5873 continue;
5875 EOL eLineEol = GetViewLineEnding(i);
5876 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5878 continue;
5880 SetViewLineEnding(i, eEol);
5882 //CUndo::EndGrouping();
5883 //CUndo::saveundostep;
5884 DocumentUpdated();
5885 SetModified();
5888 void CBaseView::SetLineEndingStyle(EOL eEol)
5890 m_lineendings = eEol;
5893 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5895 if (m_texttype == eTextType)
5897 return;
5899 m_texttype = eTextType;
5900 DocumentUpdated();
5901 SetModified();
5904 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5906 if (IsReadonly())
5907 return; // nothing to be changed in read-only view
5908 CEncodingDlg dlg;
5909 dlg.view.LoadString(nTextId);
5910 dlg.texttype = m_texttype;
5911 dlg.lineendings = GetLineEndings();
5912 if (dlg.DoModal() != IDOK)
5913 return;
5914 SetTextType(dlg.texttype);
5915 ReplaceLineEndings(dlg.lineendings);
5919 Replaces lines from source view to this
5921 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, std::function<bool(int)> fnSkip)
5923 if (!IsViewGood(pwndView))
5924 return;
5925 if (!IsWritable())
5926 return;
5927 CUndo::GetInstance().BeginGrouping();
5929 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5931 bool skip = fnSkip(viewLine);
5932 if (skip)
5934 if (GetViewMarked(viewLine))
5935 SetViewMarked(viewLine, false);
5936 continue;
5938 viewdata line = pwndView->GetViewData(viewLine);
5939 if (line.ending != EOL_NOENDING)
5940 line.ending = m_lineendings;
5941 switch (line.state)
5943 case DIFFSTATE_CONFLICTEMPTY:
5944 case DIFFSTATE_UNKNOWN:
5945 line.state = DIFFSTATE_EMPTY;
5946 case DIFFSTATE_EMPTY:
5947 break;
5948 case DIFFSTATE_ADDED:
5949 case DIFFSTATE_CONFLICTADDED:
5950 case DIFFSTATE_CONFLICTED:
5951 case DIFFSTATE_CONFLICTED_IGNORED:
5952 case DIFFSTATE_IDENTICALADDED:
5953 case DIFFSTATE_THEIRSADDED:
5954 case DIFFSTATE_YOURSADDED:
5955 case DIFFSTATE_IDENTICALREMOVED:
5956 case DIFFSTATE_REMOVED:
5957 case DIFFSTATE_THEIRSREMOVED:
5958 case DIFFSTATE_YOURSREMOVED:
5959 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5960 line.state = DIFFSTATE_NORMAL;
5961 case DIFFSTATE_NORMAL:
5962 break;
5963 default:
5964 break;
5966 bool marked = GetViewMarked(viewLine);
5967 SetViewData(viewLine, line);
5968 if (marked)
5969 SetViewMarked(viewLine, false);
5970 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5972 // if this view is in ASCII and the other is not, we have to make sure that
5973 // the text we copy from the other view can actually be saved in ASCII encoding.
5974 // if not, we have to change this views encoding to the same encoding as the other view
5975 BOOL useDefault = FALSE;
5976 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5977 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5978 SetTextType(pwndView->GetTextType());
5981 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5982 // TODO: check if copied line is same as original one set modified only when differ
5983 SetModified();
5984 SaveUndoStep();
5986 int nRemovedLines = CleanEmptyLines();
5987 SaveUndoStep();
5988 //VerifyEols();
5989 // make sure all non empty line have EOL set but last
5990 // wrong can be last copied line(have eol, but no line under),
5991 // or old last line (line before copied block missing eol, but have line under)
5992 // we'll check all lines to be sure
5993 int nLine = GetViewCount();
5994 // check last line have no EOL set
5995 while (--nLine>=0)
5997 if (!IsViewLineEmpty(nLine))
5999 if (GetViewLineEnding(nLine) != EOL_NOENDING)
6001 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6002 // so next line is empty
6003 ASSERT(IsViewLineEmpty(nLine+1));
6004 // and we can turn it to normal empty line
6005 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
6007 break;
6010 // check all (nonlast) line have EOL set
6011 while (--nLine>=0)
6013 if (!IsViewLineEmpty(nLine))
6015 if (GetViewLineEnding(nLine) == EOL_NOENDING)
6017 SetViewLineEnding(nLine, m_lineendings);
6018 // in theory there should be only one line needing fix, but most of time we get over all anyway
6019 // break;
6023 SaveUndoStep();
6024 UpdateViewLineNumbers();
6025 SaveUndoStep();
6027 CUndo::GetInstance().EndGrouping();
6029 if (nRemovedLines!=0)
6031 // some lines are gone update selection
6032 ClearSelection();
6033 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
6035 BuildAllScreen2ViewVector();
6036 pwndView->Invalidate();
6037 RefreshViews();
6040 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
6042 if (!IsWritable())
6043 return;
6044 CUndo::GetInstance().BeginGrouping();
6046 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
6047 SetViewMarked(viewLine, marked);
6049 SetModified();
6050 SaveUndoStep();
6051 CUndo::GetInstance().EndGrouping();
6053 BuildAllScreen2ViewVector();
6054 Invalidate();
6055 RefreshViews();
6058 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView *pwndView)
6060 auto fn = [this](int viewLine) -> bool { return GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6061 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6064 void CBaseView::UseViewFileOfMarked(CBaseView *pwndView)
6066 auto fn = [this](int viewLine) -> bool { return !GetViewMarked(viewLine) || GetViewState(viewLine) == DIFFSTATE_EDITED; };
6067 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6070 void CBaseView::UseViewFileExceptEdited(CBaseView *pwndView)
6072 auto fn = [this](int viewLine) -> bool { return GetViewState(viewLine) == DIFFSTATE_EDITED; };
6073 UseViewBlock(pwndView, 0, GetViewCount() - 1, fn);
6076 int CBaseView::GetIndentCharsForLine(int x, int y)
6078 const int maxGuessLine = 100;
6079 int nTabMode = -1;
6080 CString line = GetViewLine(y);
6081 if (m_nTabMode & TABMODE_SMARTINDENT)
6083 // detect left char and right char
6084 TCHAR lc = x > 0 ? line[x - 1] : '\0';
6085 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
6086 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
6087 nTabMode = 1;
6088 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
6089 nTabMode = 0;
6090 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
6091 nTabMode = m_nTabMode & TABMODE_USESPACES;
6093 // detect lines nearby
6094 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6096 bool above = i > 0 && i >= y - maxGuessLine;
6097 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6098 if (!(above || below))
6099 break;
6100 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
6101 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
6102 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
6103 nTabMode = 1;
6104 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
6105 nTabMode = 0;
6106 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
6107 nTabMode = m_nTabMode & TABMODE_USESPACES;
6110 else
6111 nTabMode = m_nTabMode & TABMODE_USESPACES;
6113 if (nTabMode > 0)
6115 // use spaces
6116 x = CountExpandedChars(line, x);
6117 return (m_nTabSize - (x % m_nTabSize));
6120 // use tab
6121 return 0;
6124 void CBaseView::AddIndentationForSelectedBlock()
6126 bool bModified = false;
6127 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6129 // skip the line if no character is selected in the last selected line
6130 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6132 continue;
6134 // skip empty lines
6135 if (IsLineEmpty(nViewLine))
6137 continue;
6139 const CString &sLine = GetViewLine(nViewLine);
6140 CString sTemp = sLine;
6141 if (sTemp.Trim().IsEmpty())
6143 // skip empty and whitechar only lines
6144 continue;
6146 // add tab to line start (alternatively m_nTabSize spaces can be used)
6147 CString tabStr;
6148 int indentChars = GetIndentCharsForLine(0, nViewLine);
6149 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
6150 SetViewLine(nViewLine, tabStr + sLine);
6151 bModified = true;
6153 if (bModified)
6155 SetModified();
6156 SaveUndoStep();
6157 BuildAllScreen2ViewVector();
6161 void CBaseView::RemoveIndentationForSelectedBlock()
6163 bool bModified = false;
6164 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6166 // skip the line if no character is selected in the last selected line
6167 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6169 continue;
6171 // skip empty lines
6172 if (IsLineEmpty(nViewLine))
6174 continue;
6176 CString sLine = GetViewLine(nViewLine);
6177 // remove up to n spaces from line start
6178 // and one tab (if less then n spaces was removed)
6179 int nPos = 0;
6180 while (nPos<m_nTabSize)
6182 switch (sLine[nPos])
6184 case ' ':
6185 nPos++;
6186 continue;
6187 case '\t':
6188 nPos++;
6190 break;
6192 if (nPos>0)
6194 sLine.Delete(0, nPos);
6195 SetViewLine(nViewLine, sLine);
6196 bModified = true;
6199 if (bModified)
6201 SetModified();
6202 SaveUndoStep();
6203 BuildAllScreen2ViewVector();
6208 there are two possible versions
6209 - convert tabs to spaces only in front of text (implemented)
6210 - convert all tabs to spaces
6212 void CBaseView::ConvertTabToSpaces()
6214 bool bModified = false;
6215 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6217 if (IsLineEmpty(nViewLine))
6219 continue;
6221 const CString &sLine = GetViewLine(nViewLine);
6222 bool bTabToConvertFound = false;
6223 int nPosIn = 0;
6224 int nPosOut = 0;
6225 while (nPosIn<sLine.GetLength())
6227 switch (sLine[nPosIn])
6229 case ' ':
6230 nPosIn++;
6231 nPosOut++;
6232 continue;
6233 case '\t':
6234 nPosIn++;
6235 bTabToConvertFound = true;
6236 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6237 continue;
6239 break;
6241 if (bTabToConvertFound)
6243 CString sLineNew = sLine;
6244 sLineNew.Delete(0, nPosIn);
6245 sLineNew = CString(' ', nPosOut) + sLineNew;
6246 SetViewLine(nViewLine, sLineNew);
6247 bModified = true;
6250 if (bModified)
6252 SetModified();
6253 SaveUndoStep();
6254 BuildAllScreen2ViewVector();
6259 there are two possible version
6260 - convert spaces to tabs only in front of text (implemented)
6261 - convert all spaces to tabs
6263 void CBaseView::Tabularize()
6265 bool bModified = false;
6266 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6268 if (IsLineEmpty(nViewLine))
6270 continue;
6272 const CString &sLine = GetViewLine(nViewLine);
6273 int nDel = 0;
6274 int nTabCount = 0; // total tabs to be used
6275 int nSpaceCount = 0; // number of spaces in tab size run
6276 int nPos = 0;
6277 while (nPos<sLine.GetLength())
6279 switch (sLine[nPos++])
6281 case ' ':
6282 //bSpace = true;
6283 if (++nSpaceCount < m_nTabSize)
6285 continue;
6287 case '\t':
6288 nTabCount++;
6289 nSpaceCount = 0;
6290 nDel = nPos;
6291 continue;
6293 break;
6295 if (nDel > 0)
6297 CString sLineNew = sLine;
6298 sLineNew.Delete(0, nDel);
6299 sLineNew = CString('\t', nTabCount) + sLineNew;
6300 if (sLine!=sLineNew)
6302 SetViewLine(nViewLine, sLineNew);
6303 bModified = true;
6307 if (bModified)
6309 SetModified();
6310 SaveUndoStep();
6311 BuildAllScreen2ViewVector();
6315 void CBaseView::RemoveTrailWhiteChars()
6317 bool bModified = false;
6318 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6320 if (IsLineEmpty(nViewLine))
6322 continue;
6324 const CString &sLine = GetViewLine(nViewLine);
6325 CString sLineNew = sLine;
6326 sLineNew.TrimRight();
6327 if (sLine.GetLength()!=sLineNew.GetLength())
6329 SetViewLine(nViewLine, sLineNew);
6330 bModified = true;
6333 if (bModified)
6335 SetModified();
6336 SaveUndoStep();
6337 BuildAllScreen2ViewVector();
6341 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6343 if (GetViewCount()>10000)
6345 // 10k lines is enough to check
6346 TWhitecharsProperties oRet = {true, true, true, true};
6347 return oRet;
6349 TWhitecharsProperties oRet = {};
6350 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6352 if (IsLineEmpty(nViewLine))
6354 continue;
6356 const CString &sLine = GetViewLine(nViewLine);
6357 if (sLine.IsEmpty())
6359 continue;
6361 // check leading whites for convertible tabs and spaces
6362 int nPos = 0;
6363 int nSpaceCount = 0; // number of spaces in tab size run
6364 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6366 switch (sLine[nPos++])
6368 case ' ':
6369 if (++nSpaceCount >= m_nTabSize)
6371 oRet.HasSpacesToConvert = true;
6373 continue;
6374 case '\t':
6375 oRet.HasTabsToConvert = true;
6376 if (nSpaceCount!=0)
6378 oRet.HasSpacesToConvert = true;
6380 continue;
6382 break;
6385 // check trailing whites for removable chars
6386 switch (sLine[sLine.GetLength()-1])
6388 case ' ':
6389 case '\t':
6390 oRet.HasTrailWhiteChars = true;
6393 // check EOLs
6394 EOL eLineEol = GetViewLineEnding(nViewLine);
6395 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6397 oRet.HasMixedEols = true;
6400 return oRet;
6403 void CBaseView::InsertText(const CString& sText)
6405 ResetUndoStep();
6407 POINT ptCaretViewPos = GetCaretViewPosition();
6408 int nLeft = ptCaretViewPos.x;
6409 int nViewLine = ptCaretViewPos.y;
6411 if ((nViewLine == 0) && (GetViewCount() == 0))
6412 OnChar(VK_RETURN, 0, 0);
6414 std::vector<CString> lines;
6415 int nStart = 0;
6416 int nEolPos = 0;
6417 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6419 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6420 lines.push_back(sLine);
6421 nEolPos++;
6422 nStart = nEolPos;
6424 CString sLine = sText.Mid(nStart);
6425 lines.push_back(sLine);
6427 int nLinesToPaste = (int)lines.size();
6428 if (nLinesToPaste > 1)
6430 // multiline text
6432 // We want to undo the multiline insertion in a single step.
6433 CUndo::GetInstance().BeginGrouping();
6435 sLine = GetViewLineChars(nViewLine);
6436 CString sLineLeft = sLine.Left(nLeft);
6437 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6438 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6439 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6440 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6442 newLine.sLine = sLineLeft + lines[0];
6443 SetViewData(nViewLine, newLine);
6446 int nInsertLine = nViewLine;
6447 for (int i = 1; i < nLinesToPaste - 1; i++)
6449 newLine.sLine = lines[i];
6450 InsertViewData(++nInsertLine, newLine);
6452 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6453 newLine.ending = eOriginalEnding;
6454 InsertViewData(++nInsertLine, newLine);
6456 SetModified();
6457 SaveUndoStep();
6459 // adds new lines everywhere except me
6460 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6462 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6464 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6466 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6468 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6470 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6472 SaveUndoStep();
6474 UpdateViewLineNumbers();
6475 CUndo::GetInstance().EndGrouping();
6477 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6479 else
6481 // single line text - just insert it
6482 sLine = GetViewLineChars(nViewLine);
6483 sLine.Insert(nLeft, sText);
6484 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6485 SetViewLine(nViewLine, sLine);
6486 SetViewState(nViewLine, DIFFSTATE_EDITED);
6487 SetModified();
6488 SaveUndoStep();
6491 RefreshViews();
6492 BuildAllScreen2ViewVector();
6493 UpdateCaretViewPosition(ptCaretViewPos);