Add ad-hoc tab mode combobox in ribbon status bar
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob32944fbfe9095e3461fff96746dadec52303dcb7
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2014 - TortoiseSVN
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
28 #include "AppUtils.h"
29 #include "GotoLineDlg.h"
30 #include "EncodingDlg.h"
32 // Note about lines:
33 // We use three different kind of lines here:
34 // 1. The real lines of the original files.
35 // These are shown in the view margin and are not used elsewhere, they're only for user information.
36 // 2. Screen lines.
37 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
38 // 3. View lines.
39 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
41 // Basically view lines are the line data, while screen lines are shown lines.
44 #ifdef _DEBUG
45 #define new DEBUG_NEW
46 #endif
48 #define MARGINWIDTH 20
49 #define HEADERHEIGHT 10
51 #define IDT_SCROLLTIMER 101
53 CBaseView * CBaseView::m_pwndLeft = NULL;
54 CBaseView * CBaseView::m_pwndRight = NULL;
55 CBaseView * CBaseView::m_pwndBottom = NULL;
56 CLocatorBar * CBaseView::m_pwndLocator = NULL;
57 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
58 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
59 CMFCRibbonStatusBar * CBaseView::m_pwndRibbonStatusBar = NULL;
60 CMainFrame * CBaseView::m_pMainFrame = NULL;
61 CBaseView::Screen2View CBaseView::m_Screen2View;
62 const UINT CBaseView::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
64 allviewstate CBaseView::m_AllState;
66 IMPLEMENT_DYNCREATE(CBaseView, CView)
68 CBaseView::CBaseView()
69 : m_pCacheBitmap(NULL)
70 , m_pViewData(NULL)
71 , m_pOtherViewData(NULL)
72 , m_pOtherView(NULL)
73 , m_nLineHeight(-1)
74 , m_nCharWidth(-1)
75 , m_nScreenChars(-1)
76 , m_nLastScreenChars(-1)
77 , m_nMaxLineLength(-1)
78 , m_nScreenLines(-1)
79 , m_nTopLine(0)
80 , m_nOffsetChar(0)
81 , m_nDigits(0)
82 , m_nMouseLine(-1)
83 , m_mouseInMargin(false)
84 , m_bIsHidden(FALSE)
85 , m_lineendings(EOL_AUTOLINE)
86 , m_bReadonly(true)
87 , m_bReadonlyIsChangable(false)
88 , m_bTarget(false)
89 , m_nCaretGoalPos(0)
90 , m_nSelViewBlockStart(-1)
91 , m_nSelViewBlockEnd(-1)
92 , m_bFocused(FALSE)
93 , m_bShowSelection(true)
94 , m_texttype(CFileTextLines::AUTOTYPE)
95 , m_bModified(FALSE)
96 , m_bOtherDiffChecked(false)
97 , m_bInlineWordDiff(true)
98 , m_bWhitespaceInlineDiffs(false)
99 , m_pState(NULL)
100 , m_pFindDialog(NULL)
101 , m_nStatusBarID(0)
102 , m_bMatchCase(false)
103 , m_bLimitToDiff(true)
104 , m_bWholeWord(false)
105 , m_pDC(NULL)
106 , m_pWorkingFile(NULL)
107 , m_bInsertMode(true)
109 m_ptCaretViewPos.x = 0;
110 m_ptCaretViewPos.y = 0;
111 m_ptSelectionViewPosStart = m_ptCaretViewPos;
112 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
113 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
114 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
115 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
116 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
117 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
118 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
119 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
120 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
121 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
122 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?"));
123 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
124 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
125 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
126 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
127 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
128 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
129 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
130 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
131 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
132 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
133 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
134 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
135 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
136 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
137 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
138 m_hMarkedIcon = LoadIcon(IDI_LINEMARKED);
139 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
141 for (int i=0; i<1024; ++i)
142 m_sConflictedText += _T("??");
143 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
145 m_szTip[0] = 0;
146 m_wszTip[0] = 0;
147 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
148 EnableToolTips();
150 m_Eols[EOL_LF] = L"\n"; // x0a
151 m_Eols[EOL_CR] = L"\r"; // x0d
152 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
153 m_Eols[EOL_LFCR] = L"\n\r";
154 m_Eols[EOL_VT] = L"\v"; // x0b
155 m_Eols[EOL_FF] = L"\f"; // x0c
156 m_Eols[EOL_NEL] = L"\x85";
157 m_Eols[EOL_LS] = L"\x2028";
158 m_Eols[EOL_PS] = L"\x2029";
159 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
160 ? EOL_CRLF
161 : m_lineendings];
162 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
163 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
166 CBaseView::~CBaseView()
168 ReleaseBitmap();
169 DeleteFonts();
170 DestroyIcon(m_hAddedIcon);
171 DestroyIcon(m_hRemovedIcon);
172 DestroyIcon(m_hConflictedIcon);
173 DestroyIcon(m_hConflictedIgnoredIcon);
174 DestroyIcon(m_hWhitespaceBlockIcon);
175 DestroyIcon(m_hEqualIcon);
176 DestroyIcon(m_hLineEndingCR);
177 DestroyIcon(m_hLineEndingCRLF);
178 DestroyIcon(m_hLineEndingLF);
179 DestroyIcon(m_hEditedIcon);
180 DestroyIcon(m_hMovedIcon);
181 DestroyIcon(m_hMarkedIcon);
182 DestroyCursor(m_margincursor);
185 BEGIN_MESSAGE_MAP(CBaseView, CView)
186 ON_WM_VSCROLL()
187 ON_WM_HSCROLL()
188 ON_WM_ERASEBKGND()
189 ON_WM_CREATE()
190 ON_WM_DESTROY()
191 ON_WM_SIZE()
192 ON_WM_MOUSEWHEEL()
193 ON_WM_MOUSEHWHEEL()
194 ON_WM_SETCURSOR()
195 ON_WM_KILLFOCUS()
196 ON_WM_SETFOCUS()
197 ON_WM_CONTEXTMENU()
198 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
199 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
200 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
201 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
202 ON_WM_KEYDOWN()
203 ON_WM_LBUTTONDOWN()
204 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
205 ON_WM_MOUSEMOVE()
206 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
207 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
208 ON_WM_CHAR()
209 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
210 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
211 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
212 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
213 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
214 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
215 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
216 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
217 ON_WM_TIMER()
218 ON_WM_LBUTTONDBLCLK()
219 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
220 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
221 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
222 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
223 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
224 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
225 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
226 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
227 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
228 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
229 ON_WM_LBUTTONUP()
230 END_MESSAGE_MAP()
233 void CBaseView::DocumentUpdated()
235 ReleaseBitmap();
236 m_nLineHeight = -1;
237 m_nCharWidth = -1;
238 m_nScreenChars = -1;
239 m_nLastScreenChars = -1;
240 m_nMaxLineLength = -1;
241 m_nScreenLines = -1;
242 m_nTopLine = 0;
243 m_bModified = FALSE;
244 m_bOtherDiffChecked = false;
245 m_nDigits = 0;
246 m_nMouseLine = -1;
247 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
248 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
249 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
250 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
251 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
252 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
253 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
254 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
255 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
256 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
257 ? EOL_CRLF
258 : m_lineendings];
259 DeleteFonts();
260 ClearCurrentSelection();
261 UpdateStatusBar();
262 Invalidate();
265 static CString GetTabModeString(int nTabMode, int nTabSize)
267 CString text;
268 if (nTabMode & TABMODE_USESPACES)
269 text = L"Space";
270 else
271 text = L"Tab";
272 text.AppendFormat(L" %d", nTabSize);
273 if (nTabMode & TABMODE_SMARTINDENT)
274 text += L" Smart";
275 return text;
278 void CBaseView::UpdateStatusBar()
280 int nRemovedLines = 0;
281 int nAddedLines = 0;
282 int nConflictedLines = 0;
284 if (m_pViewData)
286 for (int i=0; i<m_pViewData->GetCount(); i++)
288 DiffStates state = m_pViewData->GetState(i);
289 switch (state)
291 case DIFFSTATE_ADDED:
292 case DIFFSTATE_IDENTICALADDED:
293 case DIFFSTATE_THEIRSADDED:
294 case DIFFSTATE_YOURSADDED:
295 case DIFFSTATE_CONFLICTADDED:
296 nAddedLines++;
297 break;
298 case DIFFSTATE_IDENTICALREMOVED:
299 case DIFFSTATE_REMOVED:
300 case DIFFSTATE_THEIRSREMOVED:
301 case DIFFSTATE_YOURSREMOVED:
302 nRemovedLines++;
303 break;
304 case DIFFSTATE_CONFLICTED:
305 case DIFFSTATE_CONFLICTED_IGNORED:
306 nConflictedLines++;
307 break;
312 CString sBarText;
313 CString sTemp;
315 if (m_pwndStatusBar)
317 sBarText += CFileTextLines::GetEncodingName(m_texttype);
318 sBarText += sBarText.IsEmpty() ? L"" : L" ";
319 sBarText += GetEolName(m_lineendings);
320 sBarText += sBarText.IsEmpty() ? L"" : L" ";
322 if (sBarText.IsEmpty())
323 sBarText += _T(" / ");
326 if (nRemovedLines)
328 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
329 if (!sBarText.IsEmpty())
330 sBarText += _T(" / ");
331 sBarText += sTemp;
333 if (nAddedLines)
335 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
336 if (!sBarText.IsEmpty())
337 sBarText += _T(" / ");
338 sBarText += sTemp;
340 if (nConflictedLines)
342 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
343 if (!sBarText.IsEmpty())
344 sBarText += _T(" / ");
345 sBarText += sTemp;
347 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
349 if (m_pwndStatusBar)
351 UINT nID;
352 UINT nStyle;
353 int cxWidth;
354 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
356 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
358 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
360 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
361 sBarText = sTemp+sBarText;
363 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
365 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
366 sBarText = sTemp+sBarText;
368 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
369 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
370 //calculate the width of the text
371 CDC * pDC = m_pwndStatusBar->GetDC();
372 if (pDC)
374 CSize size = pDC->GetTextExtent(sBarText);
375 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
376 ReleaseDC(pDC);
378 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
380 else if (m_pwndRibbonStatusBar)
382 if (!IsViewGood(m_pwndBottom))
383 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
384 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
386 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
387 std::unique_ptr<CMFCRibbonButtonsGroup> apBtnGroupBottom(new CMFCRibbonButtonsGroup);
388 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
389 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
390 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
391 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
392 apBtnGroupBottom->AddButton(pButton);
393 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
394 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
395 apBtnGroupBottom->AddButton(pButton);
396 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, L"");
397 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMTABMODESTART);
398 apBtnGroupBottom->AddButton(pButton);
399 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
400 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
403 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
404 if (pGroup)
406 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(4));
407 if (pPane)
409 pPane->SetText(sBarText);
411 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
412 if (pButton)
414 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
415 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
417 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
418 if (pButton)
420 pButton->SetText(GetEolName(m_lineendings));
421 pButton->SetDescription(GetEolName(m_lineendings));
423 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(3));
424 if (pButton)
426 pButton->SetText(GetTabModeString(m_nTabMode, m_nTabSize));
427 pButton->SetDescription(GetTabModeString(m_nTabMode, m_nTabSize));
430 m_pwndRibbonStatusBar->RecalcLayout();
431 m_pwndRibbonStatusBar->Invalidate();
436 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
438 if (!CView::PreCreateWindow(cs))
439 return FALSE;
441 cs.dwExStyle |= WS_EX_CLIENTEDGE;
442 cs.style &= ~WS_BORDER;
443 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
444 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
446 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
447 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
449 // View must always create its own scrollbars,
450 // if only it's not used within splitter
451 cs.style |= (WS_HSCROLL | WS_VSCROLL);
453 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
454 return TRUE;
457 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
459 int nIndex = 0;
460 if (bBold)
461 nIndex |= 1;
462 if (bItalic)
463 nIndex |= 2;
464 if (m_apFonts[nIndex] == NULL)
466 m_apFonts[nIndex] = new CFont;
467 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
468 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
469 m_lfBaseFont.lfItalic = (BYTE) bItalic;
470 CDC * pDC = GetDC();
471 if (pDC)
473 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
474 ReleaseDC(pDC);
476 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
477 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
479 delete m_apFonts[nIndex];
480 m_apFonts[nIndex] = NULL;
481 return CView::GetFont();
484 return m_apFonts[nIndex];
487 void CBaseView::CalcLineCharDim()
489 CDC *pDC = GetDC();
490 if (pDC == nullptr)
491 return;
492 CFont *pOldFont = pDC->SelectObject(GetFont());
493 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
494 pDC->SelectObject(pOldFont);
495 ReleaseDC(pDC);
497 m_nLineHeight = szCharExt.cy;
498 if (m_nLineHeight <= 0)
499 m_nLineHeight = -1;
500 m_nCharWidth = szCharExt.cx;
501 if (m_nCharWidth <= 0)
502 m_nCharWidth = -1;
505 int CBaseView::GetScreenChars()
507 if (m_nScreenChars == -1)
509 CRect rect;
510 GetClientRect(&rect);
511 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
512 if (m_nScreenChars < 0)
513 m_nScreenChars = 0;
515 return m_nScreenChars;
518 int CBaseView::GetAllMinScreenChars() const
520 int nChars = INT_MAX;
521 if (IsLeftViewGood())
522 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
523 if (IsRightViewGood())
524 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
525 if (IsBottomViewGood())
526 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
527 return (nChars==INT_MAX) ? 0 : nChars;
530 int CBaseView::GetAllMaxLineLength() const
532 int nLength = 0;
533 if (IsLeftViewGood())
534 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
535 if (IsRightViewGood())
536 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
537 if (IsBottomViewGood())
538 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
539 return nLength;
542 int CBaseView::GetLineHeight()
544 if (m_nLineHeight == -1)
545 CalcLineCharDim();
546 if (m_nLineHeight <= 0)
547 return 1;
548 return m_nLineHeight;
551 int CBaseView::GetCharWidth()
553 if (m_nCharWidth == -1)
554 CalcLineCharDim();
555 if (m_nCharWidth <= 0)
556 return 1;
557 return m_nCharWidth;
560 int CBaseView::GetMaxLineLength()
562 if (m_nMaxLineLength == -1)
564 m_nMaxLineLength = 0;
565 int nLineCount = GetLineCount();
566 for (int i=0; i<nLineCount; i++)
568 int nActualLength = GetLineLength(i);
569 if (m_nMaxLineLength < nActualLength)
570 m_nMaxLineLength = nActualLength;
573 return m_nMaxLineLength;
576 int CBaseView::GetLineLength(int index)
578 if (m_pViewData == NULL)
579 return 0;
580 if (m_pViewData->GetCount() == 0)
581 return 0;
582 if ((int)m_Screen2View.size() <= index)
583 return 0;
584 int viewLine = GetViewLineForScreen(index);
585 if (m_pMainFrame->m_bWrapLines)
587 int nLineLength = GetLineChars(index).GetLength();
588 ASSERT(nLineLength >= 0);
589 return nLineLength;
591 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
592 ASSERT(nLineLength >= 0);
593 return nLineLength;
596 int CBaseView::GetViewLineLength(int nViewLine) const
598 if (m_pViewData == NULL)
599 return 0;
600 if (m_pViewData->GetCount() <= nViewLine)
601 return 0;
602 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
603 ASSERT(nLineLength >= 0);
604 return nLineLength;
607 int CBaseView::GetLineCount() const
609 if (m_pViewData == NULL)
610 return 1;
611 int nLineCount = (int)m_Screen2View.size();
612 ASSERT(nLineCount >= 0);
613 return nLineCount;
616 int CBaseView::GetSubLineOffset(int index)
618 return m_Screen2View.GetSubLineOffset(index);
621 CString CBaseView::GetViewLineChars(int nViewLine) const
623 if (m_pViewData == NULL)
624 return 0;
625 if (m_pViewData->GetCount() <= nViewLine)
626 return 0;
627 return m_pViewData->GetLine(nViewLine);
630 CString CBaseView::GetLineChars(int index)
632 if (m_pViewData == NULL)
633 return 0;
634 if (m_pViewData->GetCount() == 0)
635 return 0;
636 if ((int)m_Screen2View.size() <= index)
637 return 0;
638 int viewLine = GetViewLineForScreen(index);
639 if (m_pMainFrame->m_bWrapLines)
641 int subLine = GetSubLineOffset(index);
642 if (subLine >= 0)
644 if (subLine < CountMultiLines(viewLine))
646 return m_ScreenedViewLine[viewLine].SubLines[subLine];
648 return L"";
651 return m_pViewData->GetLine(viewLine);
654 void CBaseView::CheckOtherView()
656 if (m_bOtherDiffChecked)
657 return;
658 // find out what the 'other' file is
659 m_pOtherViewData = NULL;
660 m_pOtherView = NULL;
661 if (this == m_pwndLeft && IsRightViewGood())
663 m_pOtherViewData = m_pwndRight->m_pViewData;
664 m_pOtherView = m_pwndRight;
667 if (this == m_pwndRight && IsLeftViewGood())
669 m_pOtherViewData = m_pwndLeft->m_pViewData;
670 m_pOtherView = m_pwndLeft;
673 m_bOtherDiffChecked = true;
677 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
679 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
680 ASSERT(viewData);
682 DiffStates origstate = viewData->GetState(nLineIndex);
684 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
685 nStartBlock = nLineIndex;
686 nEndBlock = nLineIndex;
687 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
689 DiffStates state = viewData->GetState(nStartBlock - 1);
690 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
691 origstate = state;
692 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
693 nStartBlock--;
694 else
695 break;
697 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
699 DiffStates state = viewData->GetState(nEndBlock + 1);
700 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
701 origstate = state;
702 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
703 nEndBlock++;
704 else
705 break;
709 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
711 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
713 int len = 0;
714 for (int i = nStartBlock; i <= nEndBlock; ++i)
715 len += viewData->GetLine(i).GetLength()+2;
717 CString block;
718 // do not check for whitespace blocks if the line is too long, because
719 // reserving a lot of memory here takes too much time (performance hog)
720 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
721 return block;
722 block.Preallocate(len+1);
723 for (int i = nStartBlock; i <= nEndBlock; ++i)
725 block += viewData->GetLine(i);
726 block += m_Eols[viewData->GetLineEnding(i)];
728 return block;
731 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
733 if (m_pViewData == NULL)
734 return false;
735 bIdentical = false;
736 CheckOtherView();
737 if (!m_pOtherViewData)
738 return false;
739 int viewLine = GetViewLineForScreen(nLineIndex);
740 if (
741 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
742 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
745 bIdentical = true;
746 return false;
748 // first check whether the line itself only has whitespace changes
749 CString mine = m_pViewData->GetLine(viewLine);
750 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
751 if (mine.IsEmpty() && other.IsEmpty())
753 bIdentical = true;
754 return false;
757 if (mine == other)
759 bIdentical = true;
760 return true;
762 FilterWhitespaces(mine, other);
763 if (mine == other)
764 return true;
766 int nStartBlock2, nEndBlock2;
767 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
768 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
769 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
770 if (mine.IsEmpty())
771 bIdentical = false;
772 else
774 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
775 bIdentical = mine == other;
776 FilterWhitespaces(mine, other);
779 return (!mine.IsEmpty()) && (mine == other);
782 bool CBaseView::IsViewLineHidden(int nViewLine)
784 return IsViewLineHidden(m_pViewData, nViewLine);
787 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
789 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
792 int CBaseView::GetLineNumber(int index) const
794 if (m_pViewData == NULL)
795 return -1;
796 int viewLine = GetViewLineForScreen(index);
797 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
798 return -1;
799 return m_pViewData->GetLineNumber(viewLine);
802 int CBaseView::GetScreenLines()
804 if (m_nScreenLines == -1)
806 SCROLLBARINFO sbi;
807 sbi.cbSize = sizeof(sbi);
808 int scrollBarHeight = 0;
809 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
810 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
811 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
812 scrollBarHeight = 0;
813 CRect rect;
814 GetClientRect(&rect);
815 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
816 if (m_nScreenLines < 0)
817 m_nScreenLines = 0;
819 return m_nScreenLines;
822 int CBaseView::GetAllMinScreenLines() const
824 int nLines = INT_MAX;
825 if (IsLeftViewGood())
826 nLines = m_pwndLeft->GetScreenLines();
827 if (IsRightViewGood())
828 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
829 if (IsBottomViewGood())
830 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
831 return (nLines==INT_MAX) ? 0 : nLines;
834 int CBaseView::GetAllLineCount() const
836 int nLines = 0;
837 if (IsLeftViewGood())
838 nLines = m_pwndLeft->GetLineCount();
839 if (IsRightViewGood())
840 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
841 if (IsBottomViewGood())
842 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
843 return nLines;
846 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
848 if (IsLeftViewGood())
849 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
850 if (IsRightViewGood())
851 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
852 if (IsBottomViewGood())
853 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
856 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
858 SCROLLINFO si;
859 si.cbSize = sizeof(si);
860 if (bPositionOnly)
862 si.fMask = SIF_POS;
863 si.nPos = m_nTopLine;
865 else
867 EnableScrollBarCtrl(SB_VERT, TRUE);
868 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
870 m_nTopLine = 0;
871 Invalidate();
873 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
874 si.nMin = 0;
875 si.nMax = GetAllLineCount();
876 si.nPage = GetAllMinScreenLines();
877 si.nPos = m_nTopLine;
879 VERIFY(SetScrollInfo(SB_VERT, &si));
882 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
884 CView::OnVScroll(nSBCode, nPos, pScrollBar);
885 if (m_pwndLeft)
886 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
887 if (m_pwndRight)
888 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
889 if (m_pwndBottom)
890 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
891 if (m_pwndLocator)
892 m_pwndLocator->Invalidate();
895 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
897 // Note we cannot use nPos because of its 16-bit nature
898 SCROLLINFO si;
899 si.cbSize = sizeof(si);
900 si.fMask = SIF_ALL;
901 VERIFY(master->GetScrollInfo(SB_VERT, &si));
903 int nPageLines = GetScreenLines();
904 int nLineCount = GetLineCount();
906 int nNewTopLine;
908 static LONG textwidth = 0;
909 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
910 switch (nSBCode)
912 case SB_TOP:
913 nNewTopLine = 0;
914 break;
915 case SB_BOTTOM:
916 nNewTopLine = nLineCount - nPageLines + 1;
917 break;
918 case SB_LINEUP:
919 nNewTopLine = m_nTopLine - 1;
920 break;
921 case SB_LINEDOWN:
922 nNewTopLine = m_nTopLine + 1;
923 break;
924 case SB_PAGEUP:
925 nNewTopLine = m_nTopLine - si.nPage + 1;
926 break;
927 case SB_PAGEDOWN:
928 nNewTopLine = m_nTopLine + si.nPage - 1;
929 break;
930 case SB_THUMBPOSITION:
931 m_ScrollTool.Clear();
932 nNewTopLine = si.nTrackPos;
933 textwidth = 0;
934 break;
935 case SB_THUMBTRACK:
936 nNewTopLine = si.nTrackPos;
937 if (GetFocus() == this)
939 RECT thumbrect;
940 GetClientRect(&thumbrect);
941 ClientToScreen(&thumbrect);
943 POINT thumbpoint;
944 thumbpoint.x = thumbrect.right;
945 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
946 m_ScrollTool.Init(&thumbpoint);
947 if (textwidth == 0)
949 CString sTemp = sFormat;
950 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
951 textwidth = m_ScrollTool.GetTextWidth(sTemp);
953 thumbpoint.x -= textwidth;
954 int line = GetLineNumber(nNewTopLine);
955 if (line >= 0)
956 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
957 else
958 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
960 break;
961 default:
962 return;
965 if (nNewTopLine < 0)
966 nNewTopLine = 0;
967 if (nNewTopLine >= nLineCount)
968 nNewTopLine = nLineCount - 1;
969 ScrollToLine(nNewTopLine);
972 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
974 if (IsLeftViewGood())
975 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
976 if (IsRightViewGood())
977 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
978 if (IsBottomViewGood())
979 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
982 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
984 SCROLLINFO si;
985 si.cbSize = sizeof(si);
986 if (bPositionOnly)
988 si.fMask = SIF_POS;
989 si.nPos = m_nOffsetChar;
991 else
993 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
994 if (!m_pMainFrame->m_bWrapLines)
996 int minScreenChars = GetAllMinScreenChars();
997 int maxLineLength = GetAllMaxLineLength();
998 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
1000 m_nOffsetChar = 0;
1001 Invalidate();
1003 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
1004 si.nMin = 0;
1005 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
1006 si.nMax += GetMarginWidth()/GetCharWidth();
1007 si.nPage = GetScreenChars();
1008 si.nPos = m_nOffsetChar;
1011 VERIFY(SetScrollInfo(SB_HORZ, &si));
1014 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
1016 CView::OnHScroll(nSBCode, nPos, pScrollBar);
1017 if (m_pwndLeft)
1018 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1019 if (m_pwndRight)
1020 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1021 if (m_pwndBottom)
1022 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1023 if (m_pwndLocator)
1024 m_pwndLocator->Invalidate();
1027 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1029 SCROLLINFO si;
1030 si.cbSize = sizeof(si);
1031 si.fMask = SIF_ALL;
1032 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1034 int nPageChars = GetScreenChars();
1035 int nMaxLineLength = GetMaxLineLength();
1037 int nNewOffset;
1038 switch (nSBCode)
1040 case SB_LEFT:
1041 nNewOffset = 0;
1042 break;
1043 case SB_BOTTOM:
1044 nNewOffset = nMaxLineLength - nPageChars + 1;
1045 break;
1046 case SB_LINEUP:
1047 nNewOffset = m_nOffsetChar - 1;
1048 break;
1049 case SB_LINEDOWN:
1050 nNewOffset = m_nOffsetChar + 1;
1051 break;
1052 case SB_PAGEUP:
1053 nNewOffset = m_nOffsetChar - si.nPage + 1;
1054 break;
1055 case SB_PAGEDOWN:
1056 nNewOffset = m_nOffsetChar + si.nPage - 1;
1057 break;
1058 case SB_THUMBPOSITION:
1059 case SB_THUMBTRACK:
1060 nNewOffset = si.nTrackPos;
1061 break;
1062 default:
1063 return;
1066 if (nNewOffset >= nMaxLineLength)
1067 nNewOffset = nMaxLineLength - 1;
1068 if (nNewOffset < 0)
1069 nNewOffset = 0;
1070 ScrollToChar(nNewOffset, TRUE);
1073 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1075 if (m_nOffsetChar != nNewOffsetChar)
1077 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1078 m_nOffsetChar = nNewOffsetChar;
1079 CRect rcScroll;
1080 GetClientRect(&rcScroll);
1081 rcScroll.left += GetMarginWidth();
1082 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1083 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1084 // update the view header
1085 rcScroll.left = 0;
1086 rcScroll.top = 0;
1087 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1088 InvalidateRect(&rcScroll, FALSE);
1089 UpdateWindow();
1090 if (bTrackScrollBar)
1091 RecalcHorzScrollBar(TRUE);
1092 UpdateCaret();
1093 if (m_pwndLineDiffBar)
1094 m_pwndLineDiffBar->Invalidate();
1098 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1100 if (m_pwndLeft)
1101 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1102 if (m_pwndRight)
1103 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1104 if (m_pwndBottom)
1105 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1108 void CBaseView::ScrollAllSide(int delta)
1110 int nNewOffset = m_nOffsetChar;
1111 nNewOffset += delta;
1112 int nMaxLineLength = GetMaxLineLength();
1113 if (nNewOffset >= nMaxLineLength)
1114 nNewOffset = nMaxLineLength - 1;
1115 if (nNewOffset < 0)
1116 nNewOffset = 0;
1117 ScrollAllToChar(nNewOffset, TRUE);
1118 if (m_pwndLineDiffBar)
1119 m_pwndLineDiffBar->Invalidate();
1120 UpdateCaret();
1123 void CBaseView::ScrollSide(int delta)
1125 int nNewOffset = m_nOffsetChar;
1126 nNewOffset += delta;
1127 int nMaxLineLength = GetMaxLineLength();
1128 if (nNewOffset >= nMaxLineLength)
1129 nNewOffset = nMaxLineLength - 1;
1130 if (nNewOffset < 0)
1131 nNewOffset = 0;
1132 ScrollToChar(nNewOffset, TRUE);
1133 if (m_pwndLineDiffBar)
1134 m_pwndLineDiffBar->Invalidate();
1135 UpdateCaret();
1138 void CBaseView::ScrollVertical(short zDelta)
1140 const int nLineCount = GetLineCount();
1141 int nTopLine = m_nTopLine;
1142 nTopLine -= (zDelta/30);
1143 if (nTopLine < 0)
1144 nTopLine = 0;
1145 if (nTopLine >= nLineCount)
1146 nTopLine = nLineCount - 1;
1147 ScrollToLine(nTopLine, TRUE);
1150 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1152 if (m_nTopLine != nNewTopLine)
1154 if (nNewTopLine < 0)
1155 nNewTopLine = 0;
1157 int nScrollLines = m_nTopLine - nNewTopLine;
1159 m_nTopLine = nNewTopLine;
1160 CRect rcScroll;
1161 GetClientRect(&rcScroll);
1162 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1163 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1164 UpdateWindow();
1165 if (bTrackScrollBar)
1166 RecalcVertScrollBar(TRUE);
1167 UpdateCaret();
1172 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1174 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1176 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1178 int nViewLine = GetViewLineForScreen(nLineIndex);
1179 HICON icon = NULL;
1180 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1181 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1182 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1184 DiffStates state = m_pViewData->GetState(nViewLine);
1185 switch (state)
1187 case DIFFSTATE_ADDED:
1188 case DIFFSTATE_THEIRSADDED:
1189 case DIFFSTATE_YOURSADDED:
1190 case DIFFSTATE_IDENTICALADDED:
1191 case DIFFSTATE_CONFLICTADDED:
1192 eIcon = TScreenedViewLine::ICN_ADD;
1193 break;
1194 case DIFFSTATE_REMOVED:
1195 case DIFFSTATE_THEIRSREMOVED:
1196 case DIFFSTATE_YOURSREMOVED:
1197 case DIFFSTATE_IDENTICALREMOVED:
1198 eIcon = TScreenedViewLine::ICN_REMOVED;
1199 break;
1200 case DIFFSTATE_CONFLICTED:
1201 eIcon = TScreenedViewLine::ICN_CONFLICT;
1202 break;
1203 case DIFFSTATE_CONFLICTED_IGNORED:
1204 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1205 break;
1206 case DIFFSTATE_EDITED:
1207 eIcon = TScreenedViewLine::ICN_EDIT;
1208 break;
1209 default:
1210 break;
1212 bool bIdentical = false;
1213 int blockstart = -1;
1214 int blockend = -1;
1215 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1217 if (bIdentical)
1218 eIcon = TScreenedViewLine::ICN_SAME;
1219 else
1220 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1221 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1223 if (nViewLine > blockstart)
1224 Invalidate(); // redraw the upper icons since they're now changing
1225 while (blockstart <= blockend)
1226 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1229 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1230 eIcon = TScreenedViewLine::ICN_MOVED;
1231 if (m_pViewData->GetMarked(nViewLine))
1232 eIcon = TScreenedViewLine::ICN_MARKED;
1233 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1235 switch (eIcon)
1237 case TScreenedViewLine::ICN_UNKNOWN:
1238 case TScreenedViewLine::ICN_NONE:
1239 break;
1240 case TScreenedViewLine::ICN_SAME:
1241 icon = m_hEqualIcon;
1242 break;
1243 case TScreenedViewLine::ICN_EDIT:
1244 icon = m_hEditedIcon;
1245 break;
1246 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1247 icon = m_hWhitespaceBlockIcon;
1248 break;
1249 case TScreenedViewLine::ICN_ADD:
1250 icon = m_hAddedIcon;
1251 break;
1252 case TScreenedViewLine::ICN_CONFLICT:
1253 icon = m_hConflictedIcon;
1254 break;
1255 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1256 icon = m_hConflictedIgnoredIcon;
1257 break;
1258 case TScreenedViewLine::ICN_REMOVED:
1259 icon = m_hRemovedIcon;
1260 break;
1261 case TScreenedViewLine::ICN_MOVED:
1262 icon = m_hMovedIcon;
1263 break;
1264 case TScreenedViewLine::ICN_MARKED:
1265 icon = m_hMarkedIcon;
1266 break;
1270 if (icon)
1272 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1274 if ((m_bViewLinenumbers)&&(m_nDigits))
1276 int nSubLine = GetSubLineOffset(nLineIndex);
1277 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1278 CString sLinenumber;
1279 if (bIsFirstSubline)
1281 CString sLinenumberFormat;
1282 int nLineNumber = GetLineNumber(nLineIndex);
1283 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1285 // TODO: do not show if there is no number hidden
1286 // TODO: show number if there is only one
1287 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1288 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1290 else if (nLineNumber >= 0)
1292 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1293 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1295 else if (m_pMainFrame->m_bWrapLines)
1297 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1298 sLinenumber.Format(sLinenumberFormat, _T("·"));
1300 if (!sLinenumber.IsEmpty())
1302 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1303 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1305 pdc->SelectObject(GetFont());
1306 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1313 int CBaseView::GetMarginWidth()
1315 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1317 if (m_nDigits <= 0)
1319 int nLength = (int)m_pViewData->GetCount();
1320 // find out how many digits are needed to show the highest line number
1321 CString sMax;
1322 sMax.Format(_T("%d"), nLength);
1323 m_nDigits = sMax.GetLength();
1325 int nWidth = GetCharWidth();
1326 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1328 return MARGINWIDTH;
1331 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1333 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1334 COLORREF crBk, crFg;
1335 if (IsBottomViewGood())
1337 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1338 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1340 else
1342 DiffStates state = DIFFSTATE_REMOVED;
1343 if (this == m_pwndRight)
1345 state = DIFFSTATE_ADDED;
1347 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1349 pdc->SetBkColor(crBk);
1350 pdc->FillSolidRect(textrect, crBk);
1352 pdc->SetTextColor(crFg);
1354 pdc->SelectObject(GetFont(FALSE, TRUE));
1356 CString sViewTitle;
1357 if (IsModified())
1359 sViewTitle = _T("* ") + m_sWindowName;
1361 else
1363 sViewTitle = m_sWindowName;
1365 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1366 if (nStringLength > rect.Width())
1368 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1369 sViewTitle = m_sWindowName.Mid(offset);
1371 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1372 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1373 if (this->GetFocus() == this)
1374 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1375 else
1376 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1379 void CBaseView::OnDraw(CDC * pDC)
1381 CRect rcClient;
1382 GetClientRect(rcClient);
1384 int nLineCount = GetLineCount();
1385 int nLineHeight = GetLineHeight();
1387 CDC cacheDC;
1388 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1389 if (m_pCacheBitmap == NULL)
1391 m_pCacheBitmap = new CBitmap;
1392 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1394 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1396 DrawHeader(pDC, rcClient);
1398 CRect rcLine;
1399 rcLine = rcClient;
1400 rcLine.top += nLineHeight+HEADERHEIGHT;
1401 rcLine.bottom = rcLine.top + nLineHeight;
1402 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1403 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1405 int nCurrentLine = m_nTopLine;
1406 bool bBeyondFileLineCached = false;
1407 while (rcLine.top < rcClient.bottom)
1409 if (nCurrentLine < nLineCount)
1411 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1412 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1413 bBeyondFileLineCached = false;
1415 else if (!bBeyondFileLineCached)
1417 DrawMargin(&cacheDC, rcCacheMargin, -1);
1418 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1419 bBeyondFileLineCached = true;
1422 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1424 nCurrentLine ++;
1425 rcLine.OffsetRect(0, nLineHeight);
1428 cacheDC.SelectObject(pOldBitmap);
1429 cacheDC.DeleteDC();
1432 bool CBaseView::IsStateConflicted(DiffStates state)
1434 switch (state)
1436 case DIFFSTATE_CONFLICTED:
1437 case DIFFSTATE_CONFLICTED_IGNORED:
1438 case DIFFSTATE_CONFLICTEMPTY:
1439 case DIFFSTATE_CONFLICTADDED:
1440 return true;
1442 return false;
1445 bool CBaseView::IsStateEmpty(DiffStates state)
1447 switch (state)
1449 case DIFFSTATE_CONFLICTEMPTY:
1450 case DIFFSTATE_UNKNOWN:
1451 case DIFFSTATE_EMPTY:
1452 return true;
1454 return false;
1457 bool CBaseView::IsStateRemoved(DiffStates state)
1459 switch (state)
1461 case DIFFSTATE_REMOVED:
1462 case DIFFSTATE_THEIRSREMOVED:
1463 case DIFFSTATE_YOURSREMOVED:
1464 case DIFFSTATE_IDENTICALREMOVED:
1465 return true;
1467 return false;
1470 DiffStates CBaseView::ResolveState(DiffStates state)
1472 if (IsStateConflicted(state))
1474 if (state == DIFFSTATE_CONFLICTEMPTY)
1475 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1476 else
1477 return DIFFSTATE_CONFLICTRESOLVED;
1479 return state;
1483 bool CBaseView::IsLineEmpty(int nLineIndex)
1485 if (m_pViewData == 0)
1486 return FALSE;
1487 int nViewLine = GetViewLineForScreen(nLineIndex);
1488 return IsViewLineEmpty(nViewLine);
1491 bool CBaseView::IsViewLineEmpty(int nViewLine)
1493 if (m_pViewData == 0)
1494 return FALSE;
1495 const DiffStates state = m_pViewData->GetState(nViewLine);
1496 return IsStateEmpty(state);
1499 bool CBaseView::IsLineRemoved(int nLineIndex)
1501 if (m_pViewData == 0)
1502 return FALSE;
1503 int nViewLine = GetViewLineForScreen(nLineIndex);
1504 return IsViewLineRemoved(nViewLine);
1507 bool CBaseView::IsViewLineRemoved(int nViewLine)
1509 if (m_pViewData == 0)
1510 return FALSE;
1511 const DiffStates state = m_pViewData->GetState(nViewLine);
1512 return IsStateRemoved(state);
1515 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1517 if (m_pViewData == 0)
1518 return false;
1519 const DiffStates state = m_pViewData->GetState(nLineIndex);
1520 return IsStateConflicted(state);
1523 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1525 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1528 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1530 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1533 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1535 if (origin.x < (rc.left - GetCharWidth() +1))
1536 return;
1537 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1538 return;
1539 int viewLine = GetViewLineForScreen(nLineIndex);
1540 EOL ending = m_pViewData->GetLineEnding(viewLine);
1541 if (m_bIconLFs)
1543 HICON hEndingIcon = NULL;
1544 switch (ending)
1546 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1547 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1548 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1549 default: return;
1551 // If EOL style has changed, color end-of-line markers as inline differences.
1553 m_bShowInlineDiff && m_pOtherViewData &&
1554 (viewLine < m_pOtherViewData->GetCount()) &&
1555 (ending != EOL_NOENDING) &&
1556 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1557 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1560 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1563 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1565 else
1567 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1568 CPen * oldpen = pDC->SelectObject(&pen);
1569 int yMiddle = origin.y + rc.Height()/2;
1570 int xMiddle = origin.x+GetCharWidth()/2;
1571 bool bMultiline = false;
1572 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1574 if (GetLineLength(nLineIndex+1))
1576 // multiline
1577 bMultiline = true;
1578 pDC->MoveTo(origin.x, yMiddle-2);
1579 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1580 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1581 pDC->LineTo(origin.x, yMiddle+2);
1583 else if (GetLineLength(nLineIndex) == 0)
1584 bMultiline = true;
1586 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1587 bMultiline = true;
1589 if (!bMultiline)
1591 switch (ending)
1593 case EOL_AUTOLINE:
1594 case EOL_CRLF:
1595 // arrow from top to middle+2, then left
1596 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1597 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1598 case EOL_CR:
1599 // arrow from right to left
1600 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1601 pDC->LineTo(origin.x, yMiddle);
1602 pDC->LineTo(origin.x+4, yMiddle+4);
1603 pDC->MoveTo(origin.x, yMiddle);
1604 pDC->LineTo(origin.x+4, yMiddle-4);
1605 break;
1606 case EOL_LFCR:
1607 // from right-upper to left then down
1608 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1609 pDC->LineTo(xMiddle, yMiddle-2);
1610 pDC->LineTo(xMiddle, rc.bottom-1);
1611 pDC->LineTo(xMiddle+4, rc.bottom-5);
1612 pDC->MoveTo(xMiddle, rc.bottom-1);
1613 pDC->LineTo(xMiddle-4, rc.bottom-5);
1614 break;
1615 case EOL_LF:
1616 // arrow from top to bottom
1617 pDC->MoveTo(xMiddle, rc.top);
1618 pDC->LineTo(xMiddle, rc.bottom-1);
1619 pDC->LineTo(xMiddle+4, rc.bottom-5);
1620 pDC->MoveTo(xMiddle, rc.bottom-1);
1621 pDC->LineTo(xMiddle-4, rc.bottom-5);
1622 break;
1623 case EOL_FF: // Form Feed, U+000C
1624 case EOL_NEL: // Next Line, U+0085
1625 case EOL_LS: // Line Separator, U+2028
1626 case EOL_PS: // Paragraph Separator, U+2029
1627 // draw a horizontal line at the bottom of this line
1628 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1629 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1630 pDC->LineTo(origin.x, rc.bottom-2);
1631 pDC->LineTo(origin.x+5, rc.bottom-2);
1632 pDC->MoveTo(origin.x, rc.bottom-2);
1633 pDC->LineTo(origin.x+1, rc.bottom-6);
1634 break;
1635 default: // other EOLs
1636 // arrow from top right to bottom left
1637 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1638 pDC->LineTo(origin.x, rc.bottom-1);
1639 pDC->LineTo(origin.x+5, rc.bottom-2);
1640 pDC->MoveTo(origin.x, rc.bottom-1);
1641 pDC->LineTo(origin.x+1, rc.bottom-6);
1642 break;
1643 case EOL_NOENDING:
1644 break;
1647 pDC->SelectObject(oldpen);
1651 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1653 if (!m_bShowSelection)
1654 return;
1656 int nSelBlockStart;
1657 int nSelBlockEnd;
1658 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1659 return;
1661 const int THICKNESS = 2;
1662 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1664 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1665 int nSubLine = GetSubLineOffset(nLineIndex);
1666 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1667 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1669 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1672 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1673 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1675 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1679 void CBaseView::DrawTextLine(
1680 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1682 ASSERT(nLineIndex < GetLineCount());
1683 int nViewLine = GetViewLineForScreen(nLineIndex);
1684 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1686 LineColors lineCols = GetLineColors(nViewLine);
1688 CString sViewLine = GetViewLineChars(nViewLine);
1689 // mark selection
1690 if (m_bShowSelection && HasTextSelection())
1692 // has this line selection ?
1693 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1695 int nViewLineLength = sViewLine.GetLength();
1697 // first suppose the whole line is selected
1698 int selectedStart = 0;
1699 int selectedEnd = nViewLineLength;
1701 // the view line is partially selected
1702 if (m_ptSelectionViewPosStart.y == nViewLine)
1704 selectedStart = m_ptSelectionViewPosStart.x;
1707 if (m_ptSelectionViewPosEnd.y == nViewLine)
1709 selectedEnd = m_ptSelectionViewPosEnd.x;
1711 // apply selection coloring
1712 // First enforce start and end point
1713 lineCols.SplitBlock(selectedStart);
1714 lineCols.SplitBlock(selectedEnd);
1715 // change color of affected parts
1716 long intenseColorScale = m_bFocused ? 70 : 30;
1717 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1718 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1720 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1721 if (it->second.shot == it->second.background)
1723 it->second.shot = crBk;
1725 it->second.background = crBk;
1726 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1731 // TODO: remove duplicate from selection and mark
1732 if (!m_sMarkedWord.IsEmpty())
1734 int nMarkLength = m_sMarkedWord.GetLength();
1735 //int nViewLineLength = sViewLine.GetLength();
1736 const TCHAR * text = sViewLine;
1737 const TCHAR * findText = text;
1738 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1740 int nMarkStart = static_cast<int>(findText - text);
1741 int nMarkEnd = nMarkStart + nMarkLength;
1742 findText += nMarkLength;
1743 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1744 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1745 if (eLeft == eStart)
1746 continue;
1747 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1748 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1749 if (eRight == eEnd)
1750 continue;
1752 // First enforce start and end point
1753 lineCols.SplitBlock(nMarkStart);
1754 lineCols.SplitBlock(nMarkEnd);
1755 // change color of affected parts
1756 const long int nIntenseColorScale = 200;
1757 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1758 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1760 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1761 if (it->second.shot == it->second.background)
1763 it->second.shot = crBk;
1765 it->second.background = crBk;
1766 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1770 if (!m_sFindText.IsEmpty())
1772 int nMarkStart = 0;
1773 int nMarkEnd = 0;
1774 int nStringPos = nMarkStart;
1775 CString searchLine = sViewLine;
1776 if (!m_bMatchCase)
1777 searchLine.MakeLower();
1778 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1780 // First enforce start and end point
1781 lineCols.SplitBlock(nMarkStart+nStringPos);
1782 lineCols.SplitBlock(nMarkEnd+nStringPos);
1783 // change color of affected parts
1784 const long int nIntenseColorScale = 30;
1785 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1786 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1788 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1789 if (it->second.shot == it->second.background)
1791 it->second.shot = crBk;
1793 it->second.background = crBk;
1794 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1796 searchLine = searchLine.Mid(nMarkEnd);
1797 nStringPos = nMarkEnd;
1798 nMarkStart = 0;
1799 nMarkEnd = 0;
1803 // @ this point we may cache data for next line which may be same in wrapped mode
1805 int nTextOffset = 0;
1806 int nSubline = GetSubLineOffset(nLineIndex);
1807 for (int n=0; n<nSubline; n++)
1809 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1810 nTextOffset += sLine.GetLength();
1813 CString sLine = GetLineChars(nLineIndex);
1814 int nLineLength = sLine.GetLength();
1815 CString sLineExp = ExpandChars(sLine);
1816 LPCTSTR textExp = sLineExp;
1817 //int nLineLengthExp = sLineExp.GetLength();
1818 int nStartExp = 0;
1819 int nLeft = coords.x;
1820 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1822 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1823 ++itEnd;
1824 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1825 int nEnd = nLineLength;
1826 if (itEnd != lineCols.end())
1828 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1830 int nBlockLength = nEnd - nStart;
1831 if (nBlockLength > 0 && nEnd>=0)
1833 pDC->SetBkColor(itStart->second.background);
1834 pDC->SetTextColor(itStart->second.text);
1835 int nEndExp = CountExpandedChars(sLine, nEnd);
1836 int nTextLength = nEndExp - nStartExp;
1837 LPCTSTR p_zBlockText = textExp + nStartExp;
1838 SIZE Size;
1839 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1840 int nRight = nLeft + Size.cx;
1841 if ((nRight > rc.left) && (nLeft < rc.right))
1843 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1844 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1845 // is 4094 (4095 doesn't work anymore).
1846 // So we limit the length here to that 4094 chars.
1847 // In case we're scrolled to the right, there's no need to draw the string
1848 // from way outside our window, so we also offset the drawing to the start of the window.
1849 // This reduces the string length as well.
1850 int offset = 0;
1851 int leftcoord = nLeft;
1852 if (nLeft < 0)
1854 int fit = nTextLength;
1855 std::unique_ptr<int> posBuffer(new int[fit]);
1856 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1857 int lower = 0, upper = fit - 1;
1860 int middle = (upper + lower + 1) / 2;
1861 int width = posBuffer.get()[middle];
1862 if (rc.left - nLeft < width)
1863 upper = middle - 1;
1864 else
1865 lower = middle;
1866 } while (lower < upper);
1868 offset = lower;
1869 nTextLength -= offset;
1870 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1873 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1874 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1876 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1879 nLeft = nRight;
1880 coords.x = nRight;
1881 nStartExp = nEndExp;
1886 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1888 if (nLineIndex >= GetLineCount())
1889 nLineIndex = -1;
1890 ASSERT(nLineIndex >= -1);
1892 if ((nLineIndex == -1) || !m_pViewData)
1894 // Draw line beyond the text
1895 COLORREF crBkgnd, crText;
1896 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1897 pDC->FillSolidRect(rc, crBkgnd);
1898 return;
1901 int viewLine = GetViewLineForScreen(nLineIndex);
1902 if (m_pMainFrame->m_bCollapsed)
1904 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1906 COLORREF crBkgnd, crText;
1907 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1908 pDC->FillSolidRect(rc, crBkgnd);
1910 const int THICKNESS = 2;
1911 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1912 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1913 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1914 pDC->SetBkColor(crBkgnd);
1915 CRect rect = rc;
1916 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1917 return;
1921 DiffStates diffState = m_pViewData->GetState(viewLine);
1922 COLORREF crBkgnd, crText;
1923 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1925 if (diffState == DIFFSTATE_CONFLICTED)
1927 // conflicted lines are shown without 'text' on them
1928 CRect rect = rc;
1929 pDC->FillSolidRect(rc, crBkgnd);
1930 // now draw some faint text patterns
1931 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1932 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1933 DrawBlockLine(pDC, rc, nLineIndex);
1934 return;
1937 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1938 CString sLine = GetLineChars(nLineIndex);
1939 if (sLine.IsEmpty())
1941 pDC->FillSolidRect(rc, crBkgnd);
1942 DrawBlockLine(pDC, rc, nLineIndex);
1943 DrawLineEnding(pDC, rc, nLineIndex, origin);
1944 return;
1947 CheckOtherView();
1949 // Draw the line
1951 pDC->SelectObject(GetFont(FALSE, FALSE));
1953 DrawTextLine(pDC, rc, nLineIndex, origin);
1955 // draw white space after the end of line
1956 CRect frect = rc;
1957 if (origin.x > frect.left)
1958 frect.left = origin.x;
1959 if (frect.right > frect.left)
1960 pDC->FillSolidRect(frect, crBkgnd);
1962 // draw the whitespace chars
1963 LPCTSTR pszChars = (LPCWSTR)sLine;
1964 if (m_bViewWhitespace)
1966 int xpos = 0;
1967 int nChars = 0;
1968 LPCTSTR pLastSpace = pszChars;
1969 int y = rc.top + (rc.bottom-rc.top)/2;
1970 xpos -= m_nOffsetChar * GetCharWidth();
1972 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1973 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1974 while (*pszChars)
1976 switch (*pszChars)
1978 case _T('\t'):
1980 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
1981 pLastSpace = pszChars + 1;
1982 // draw an arrow
1983 int nSpaces = GetTabSize() - nChars % GetTabSize();
1984 if (xpos + nSpaces * GetCharWidth() >= 0)
1986 CPen * oldPen = pDC->SelectObject(&pen);
1987 pDC->MoveTo(xpos + rc.left, y);
1988 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-2, y);
1989 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-6, y-4);
1990 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left-2, y);
1991 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-6, y+4);
1992 pDC->SelectObject(oldPen);
1994 xpos += nSpaces * GetCharWidth();
1995 nChars += nSpaces;
1997 break;
1998 case _T(' '):
2000 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
2001 pLastSpace = pszChars + 1;
2002 // draw a small dot
2003 CPen * oldPen = pDC->SelectObject(&pen2);
2004 if (xpos + GetCharWidth() >= 0)
2006 pDC->MoveTo(xpos + rc.left + GetCharWidth()/2-1, y);
2007 pDC->LineTo(xpos + rc.left + GetCharWidth()/2+1, y);
2008 pDC->SelectObject(oldPen);
2010 xpos += GetCharWidth();
2011 nChars++;
2013 break;
2014 default:
2015 nChars++;
2016 break;
2018 pszChars++;
2021 DrawBlockLine(pDC, rc, nLineIndex);
2022 if (origin.x >= rc.left)
2023 DrawLineEnding(pDC, rc, nLineIndex, origin);
2026 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2028 if (nCount <= 0)
2030 line = _T("");
2031 return;
2034 int nTabSize = GetTabSize();
2036 int nActualOffset = CountExpandedChars(sLine, nOffset);
2038 LPCTSTR pszChars = (LPCWSTR)sLine;
2039 pszChars += nOffset;
2040 int nLength = nCount;
2042 int nTabCount = 0;
2043 for (int i=0; i<nLength; i++)
2045 if (pszChars[i] == _T('\t'))
2046 nTabCount ++;
2049 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2050 int nCurPos = 0;
2051 if (nTabCount > 0 || m_bViewWhitespace)
2053 for (int i=0; i<nLength; i++)
2055 if (pszChars[i] == _T('\t'))
2057 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2058 while (nSpaces > 0)
2060 pszBuf[nCurPos ++] = _T(' ');
2061 nSpaces --;
2064 else
2066 pszBuf[nCurPos] = pszChars[i];
2067 nCurPos ++;
2071 else
2073 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2074 nCurPos = nLength;
2076 pszBuf[nCurPos] = 0;
2077 line.ReleaseBuffer();
2080 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2082 CString sRet;
2083 int nLength = sLine.GetLength();
2084 ExpandChars(sLine, nOffset, nLength, sRet);
2085 return sRet;
2088 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2090 int nTabSize = GetTabSize();
2092 int nActualOffset = 0;
2093 for (int i=0; i<nLength; i++)
2095 if (sLine[i] == _T('\t'))
2096 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2097 else
2098 nActualOffset ++;
2100 return nActualOffset;
2103 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2105 if (m_pwndLeft)
2106 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2107 if (m_pwndRight)
2108 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2109 if (m_pwndBottom)
2110 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2111 if (m_pwndLocator)
2112 m_pwndLocator->Invalidate();
2115 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2117 //almost the same as ScrollAllToLine, but try to put the line in the
2118 //middle of the view, not on top
2119 int nNewTopLine = nNewLine - GetScreenLines()/2;
2120 if (nNewTopLine < 0)
2121 nNewTopLine = 0;
2122 if (nNewTopLine >= (int)m_Screen2View.size())
2123 nNewTopLine = (int)m_Screen2View.size()-1;
2124 if (bAll)
2125 ScrollAllToLine(nNewTopLine);
2126 else
2127 ScrollToLine(nNewTopLine);
2130 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2132 return TRUE;
2135 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2137 if (CView::OnCreate(lpCreateStruct) == -1)
2138 return -1;
2140 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2141 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2142 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2143 m_lfBaseFont.lfHeight = 0;
2144 m_lfBaseFont.lfWeight = FW_NORMAL;
2145 m_lfBaseFont.lfItalic = FALSE;
2146 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2147 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2148 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2149 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2150 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2152 return 0;
2155 void CBaseView::OnDestroy()
2157 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2159 m_pFindDialog->SendMessage(WM_CLOSE);
2160 return;
2162 CView::OnDestroy();
2163 DeleteFonts();
2164 ReleaseBitmap();
2167 void CBaseView::OnSize(UINT nType, int cx, int cy)
2169 CView::OnSize(nType, cx, cy);
2170 ReleaseBitmap();
2172 m_nScreenLines = -1;
2173 m_nScreenChars = -1;
2174 if (m_nLastScreenChars != GetScreenChars())
2176 BuildAllScreen2ViewVector();
2177 m_nLastScreenChars = m_nScreenChars;
2178 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2180 // if we're in wrap mode, the line wrapping most likely changed
2181 // and that means we have to redraw the whole window, not just the
2182 // scrolled part.
2183 Invalidate(FALSE);
2185 else
2187 // make sure the view header is redrawn
2188 CRect rcScroll;
2189 GetClientRect(&rcScroll);
2190 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2191 InvalidateRect(&rcScroll, FALSE);
2194 else
2196 // make sure the view header is redrawn
2197 CRect rcScroll;
2198 GetClientRect(&rcScroll);
2199 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2200 InvalidateRect(&rcScroll, FALSE);
2202 UpdateLocator();
2203 RecalcVertScrollBar();
2204 RecalcHorzScrollBar();
2206 UpdateCaret();
2209 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2211 if (m_pwndLeft)
2212 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2213 if (m_pwndRight)
2214 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2215 if (m_pwndBottom)
2216 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2217 if (m_pwndLocator)
2218 m_pwndLocator->Invalidate();
2219 return CView::OnMouseWheel(nFlags, zDelta, pt);
2222 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2224 if (m_pwndLeft)
2225 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2226 if (m_pwndRight)
2227 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2228 if (m_pwndBottom)
2229 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2230 if (m_pwndLocator)
2231 m_pwndLocator->Invalidate();
2234 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2236 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2237 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2239 if (bControl || bShift)
2241 if (m_pMainFrame->m_bWrapLines)
2242 return;
2243 // Ctrl-Wheel scrolls sideways
2244 ScrollSide(-zDelta/30);
2246 else
2248 ScrollVertical(zDelta);
2252 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2254 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2255 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2257 if (bControl || bShift)
2259 ScrollVertical(zDelta);
2261 else
2263 if (m_pMainFrame->m_bWrapLines)
2264 return;
2265 // Ctrl-Wheel scrolls sideways
2266 ScrollSide(-zDelta/30);
2270 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2272 if (nHitTest == HTCLIENT)
2274 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2276 if (m_nMouseLine < (int)m_Screen2View.size())
2278 if (m_nMouseLine >= 0)
2280 int viewLine = GetViewLineForScreen(m_nMouseLine);
2281 if (viewLine < m_pViewData->GetCount())
2283 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2285 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2286 return TRUE;
2292 if (m_mouseInMargin)
2294 ::SetCursor(m_margincursor);
2295 return TRUE;
2297 if (m_nMouseLine >= 0)
2299 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2300 return TRUE;
2303 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2304 return TRUE;
2306 return CView::OnSetCursor(pWnd, nHitTest, message);
2309 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2311 CView::OnKillFocus(pNewWnd);
2312 m_bFocused = FALSE;
2313 UpdateCaret();
2314 Invalidate();
2317 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2319 CView::OnSetFocus(pOldWnd);
2320 m_bFocused = TRUE;
2321 UpdateCaret();
2322 Invalidate();
2325 int CBaseView::GetLineFromPoint(CPoint point)
2327 ScreenToClient(&point);
2328 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2331 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2333 if (!this->IsWindowVisible())
2334 return;
2336 CIconMenu popup;
2337 if (!popup.CreatePopupMenu())
2338 return;
2340 AddContextItems(popup, state);
2342 CMenu popupEols;
2343 CMenu popupUnicode;
2344 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2345 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2346 if (IsWritable())
2348 CString temp;
2349 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2350 temp.LoadString(IDS_EDIT_TAB2SPACE);
2351 popup.AppendMenu((MF_STRING | oWhites.HasTabsToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2352 temp.LoadString(IDS_EDIT_SPACE2TAB);
2353 popup.AppendMenu((MF_STRING | oWhites.HasSpacesToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2354 temp.LoadString(IDS_EDIT_TRIM);
2355 popup.AppendMenu((MF_STRING | oWhites.HasTrailWhiteChars) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2357 // add eol submenu
2358 if (!popupEols.CreatePopupMenu())
2359 return;
2361 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2362 for (int i = 1; i < _countof(eolArray); i++)
2364 CString temp = GetEolName(eolArray[i]);
2365 bool bChecked = (eEolType == eolArray[i]);
2366 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2369 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2370 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2372 // add encoding submenu
2373 if (!popupUnicode.CreatePopupMenu())
2374 return;
2375 for (int i = 0; i < _countof(uctArray); i++)
2377 CString temp = CFileTextLines::GetEncodingName(uctArray[i]);
2378 bool bChecked = (m_texttype == uctArray[i]);
2379 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2381 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2382 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2386 CompensateForKeyboard(point);
2388 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2389 ResetUndoStep();
2390 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2392 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2394 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2396 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2397 SaveUndoStep();
2399 switch (cmd)
2401 // 2-pane view commands; target is right view
2402 case POPUPCOMMAND_USELEFTBLOCK:
2403 m_pwndRight->UseLeftBlock();
2404 break;
2405 case POPUPCOMMAND_USELEFTFILE:
2406 m_pwndRight->UseLeftFile();
2407 break;
2408 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2409 m_pwndRight->UseBothLeftFirst();
2410 break;
2411 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2412 m_pwndRight->UseBothRightFirst();
2413 break;
2414 case POPUPCOMMAND_MARKBLOCK:
2415 m_pwndRight->MarkBlock(true);
2416 break;
2417 case POPUPCOMMAND_UNMARKBLOCK:
2418 m_pwndRight->MarkBlock(false);
2419 break;
2420 case POPUPCOMMAND_USELEFTFILEEXCEPTMARKED:
2421 m_pwndRight->UseLeftFileExceptMarked();
2422 break;
2423 // 2-pane view multiedit commands; target is left view
2424 case POPUPCOMMAND_PREPENDFROMRIGHT:
2425 if (!m_pwndLeft->IsReadonly())
2426 m_pwndLeft->UseBothRightFirst();
2427 break;
2428 case POPUPCOMMAND_REPLACEBYRIGHT:
2429 if (!m_pwndLeft->IsReadonly())
2430 m_pwndLeft->UseRightBlock();
2431 break;
2432 case POPUPCOMMAND_APPENDFROMRIGHT:
2433 if (!m_pwndLeft->IsReadonly())
2434 m_pwndLeft->UseBothLeftFirst();
2435 break;
2436 case POPUPCOMMAND_USERIGHTFILE:
2437 m_pwndLeft->UseRightFile();
2438 break;
2439 // 3-pane view commands; target is bottom view
2440 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2441 m_pwndBottom->UseBothRightFirst();
2442 break;
2443 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2444 m_pwndBottom->UseBothLeftFirst();
2445 break;
2446 case POPUPCOMMAND_USEYOURBLOCK:
2447 m_pwndBottom->UseRightBlock();
2448 break;
2449 case POPUPCOMMAND_USEYOURFILE:
2450 m_pwndBottom->UseRightFile();
2451 break;
2452 case POPUPCOMMAND_USETHEIRBLOCK:
2453 m_pwndBottom->UseLeftBlock();
2454 break;
2455 case POPUPCOMMAND_USETHEIRFILE:
2456 m_pwndBottom->UseLeftFile();
2457 break;
2458 // copy, cut and paste commands
2459 case ID_EDIT_COPY:
2460 OnEditCopy();
2461 break;
2462 case ID_EDIT_CUT:
2463 OnEditCut();
2464 break;
2465 case ID_EDIT_PASTE:
2466 OnEditPaste();
2467 break;
2468 // white chars manipulations
2469 case POPUPCOMMAND_TABTOSPACES:
2470 ConvertTabToSpaces();
2471 break;
2472 case POPUPCOMMAND_SPACESTOTABS:
2473 Tabularize();
2474 break;
2475 case POPUPCOMMAND_REMOVETRAILWHITES:
2476 RemoveTrailWhiteChars();
2477 break;
2478 default:
2479 return;
2480 } // switch (cmd)
2481 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2482 return;
2485 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2487 if (!m_pViewData)
2488 return;
2490 int nViewBlockStart = -1;
2491 int nViewBlockEnd = -1;
2492 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2493 if ((point.x >= 0) && (point.y >= 0))
2495 int nLine = GetLineFromPoint(point)-1;
2496 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2498 int nViewLine = GetViewLineForScreen(nLine);
2499 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2501 ClearSelection(); // Clear text-copy selection
2503 nViewBlockStart = nViewLine;
2504 nViewBlockEnd = nViewLine;
2505 DiffStates state = m_pViewData->GetState(nViewLine);
2506 while (nViewBlockStart > 0)
2508 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2509 if (!LinesInOneChange(-1, state, lineState))
2510 break;
2511 nViewBlockStart--;
2514 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2516 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2517 if (!LinesInOneChange(1, state, lineState))
2518 break;
2519 nViewBlockEnd++;
2522 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2523 UpdateCaretPosition(point);
2528 // FixSelection(); fix selection range
2529 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2530 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2532 DiffStates state = DIFFSTATE_UNKNOWN;
2533 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2535 // find a more 'relevant' state in the selection
2536 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2538 state = m_pViewData->GetState(i);
2539 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2540 break;
2543 OnContextMenu(point, state);
2546 void CBaseView::RefreshViews()
2548 if (m_pwndLeft)
2550 m_pwndLeft->UpdateStatusBar();
2551 m_pwndLeft->Invalidate();
2553 if (m_pwndRight)
2555 m_pwndRight->UpdateStatusBar();
2556 m_pwndRight->Invalidate();
2558 if (m_pwndBottom)
2560 m_pwndBottom->UpdateStatusBar();
2561 m_pwndBottom->Invalidate();
2563 if (m_pwndLocator)
2564 m_pwndLocator->Invalidate();
2567 void CBaseView::GoToFirstDifference()
2569 SetCaretToFirstViewLine();
2570 SelectNextBlock(1, false, false);
2573 void CBaseView::GoToFirstConflict()
2575 SetCaretToFirstViewLine();
2576 SelectNextBlock(1, true, false);
2579 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2581 ClearSelection();
2582 SetupAllSelection(nStart, max(nStart, nEnd));
2584 UpdateCaretPosition(SetupPoint(0, nStart));
2585 Invalidate();
2588 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2590 ClearSelection();
2591 SetupAllViewSelection(nStart, max(nStart, nEnd));
2593 UpdateCaretViewPosition(SetupPoint(0, nStart));
2594 Invalidate();
2597 void CBaseView::SetupAllViewSelection(int start, int end)
2599 SetupViewSelection(m_pwndBottom, start, end);
2600 SetupViewSelection(m_pwndLeft, start, end);
2601 SetupViewSelection(m_pwndRight, start, end);
2604 void CBaseView::SetupAllSelection(int start, int end)
2606 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2609 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2611 void CBaseView::SetupSelection(int start, int end)
2613 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2616 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2618 if (!IsViewGood(view))
2619 return;
2620 view->SetupViewSelection(start, end);
2623 void CBaseView::SetupViewSelection(int start, int end)
2625 // clear text selection before setting line selection ?
2626 m_nSelViewBlockStart = start;
2627 m_nSelViewBlockEnd = end;
2628 Invalidate();
2632 void CBaseView::OnMergePreviousconflict()
2634 SelectNextBlock(-1, true);
2637 void CBaseView::OnMergeNextconflict()
2639 SelectNextBlock(1, true);
2642 void CBaseView::OnMergeNextdifference()
2644 SelectNextBlock(1, false);
2647 void CBaseView::OnMergePreviousdifference()
2649 SelectNextBlock(-1, false);
2652 bool CBaseView::HasNextConflict()
2654 return SelectNextBlock(1, true, true, true);
2657 bool CBaseView::HasPrevConflict()
2659 return SelectNextBlock(-1, true, true, true);
2662 bool CBaseView::HasNextDiff()
2664 return SelectNextBlock(1, false, true, true);
2667 bool CBaseView::HasPrevDiff()
2669 return SelectNextBlock(-1, false, true, true);
2672 bool CBaseView::LinesInOneChange(int direction,
2673 DiffStates initialLineState, DiffStates currentLineState)
2675 // Checks whether all the adjacent lines starting from the initial line
2676 // and up to the current line form the single change
2678 // First of all, if the two lines have identical states, they surely
2679 // belong to one change.
2680 if (initialLineState == currentLineState)
2681 return true;
2683 // Either we move down and initial line state is "added" or "removed" and
2684 // current line state is "empty"...
2685 if (direction > 0)
2687 if (currentLineState == DIFFSTATE_EMPTY)
2689 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2690 return true;
2692 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2693 return true;
2695 // ...or we move up and initial line state is "empty" and current line
2696 // state is "added" or "removed".
2697 if (direction < 0)
2699 if (initialLineState == DIFFSTATE_EMPTY)
2701 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2702 return true;
2704 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2705 return true;
2707 return false;
2710 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2712 if (! m_pViewData)
2713 return false;
2715 const int linesCount = (int)m_Screen2View.size();
2716 if(linesCount == 0)
2717 return false;
2719 int nCenterPos = GetCaretPosition().y;
2720 int nLimit = -1;
2721 if (nDirection > 0)
2722 nLimit = linesCount;
2724 if (nCenterPos >= linesCount)
2725 nCenterPos = linesCount-1;
2727 if (bSkipEndOfCurrentBlock)
2729 // Find end of current block
2730 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2731 while (nCenterPos != nLimit)
2733 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2734 if (!LinesInOneChange(nDirection, state, lineState))
2735 break;
2736 nCenterPos += nDirection;
2740 // Find next diff/conflict block
2741 while (nCenterPos != nLimit)
2743 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2744 if (!bConflict &&
2745 (linestate != DIFFSTATE_NORMAL) &&
2746 (linestate != DIFFSTATE_UNKNOWN))
2748 break;
2750 if (bConflict &&
2751 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2752 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2753 (linestate == DIFFSTATE_CONFLICTED) ||
2754 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2756 break;
2759 nCenterPos += nDirection;
2761 if (nCenterPos == nLimit)
2762 return false;
2763 if (dryrun)
2764 return (nCenterPos != nLimit);
2766 // Find end of new block
2767 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2768 int nBlockEnd = nCenterPos;
2769 const int maxAllowedLine = nLimit-nDirection;
2770 while (nBlockEnd != maxAllowedLine)
2772 const int lineIndex = nBlockEnd + nDirection;
2773 if (lineIndex >= linesCount)
2774 break;
2775 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2776 if (!LinesInOneChange(nDirection, state, lineState))
2777 break;
2778 nBlockEnd += nDirection;
2781 int nTopPos = nCenterPos - (GetScreenLines()/2);
2782 if (nTopPos < 0)
2783 nTopPos = 0;
2785 POINT ptCaretPos = {0, nCenterPos};
2786 SetCaretPosition(ptCaretPos);
2787 ClearSelection();
2788 if (nDirection > 0)
2789 SetupAllSelection(nCenterPos, nBlockEnd);
2790 else
2791 SetupAllSelection(nBlockEnd, nCenterPos);
2793 ScrollAllToLine(nTopPos, FALSE);
2794 RecalcAllVertScrollBars(TRUE);
2795 SetCaretToLineStart();
2796 EnsureCaretVisible();
2797 OnNavigateNextinlinediff();
2799 UpdateViewsCaretPosition();
2800 UpdateCaret();
2801 ShowDiffLines(nCenterPos);
2802 return true;
2805 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2807 if (pNMHDR->idFrom != (UINT)m_hWnd)
2808 return FALSE;
2810 CString strTipText;
2811 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2813 DWORD pos = GetMessagePos();
2814 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2815 ScreenToClient(&point);
2816 const int nLine = GetButtonEventLineIndex(point);
2818 if (nLine >= 0)
2820 int nViewLine = GetViewLineForScreen(nLine);
2821 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2823 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2824 if (movedIndex >= 0)
2826 if (m_pViewData->IsMovedFrom(nViewLine))
2828 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2830 else
2832 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2839 *pResult = 0;
2840 if (strTipText.IsEmpty())
2841 return TRUE;
2843 // need to handle both ANSI and UNICODE versions of the message
2844 if (pNMHDR->code == TTN_NEEDTEXTA)
2846 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2847 pTTTA->lpszText = m_szTip;
2848 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2850 else
2852 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2853 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2854 pTTTW->lpszText = m_wszTip;
2857 return TRUE; // message was handled
2860 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2862 CRect rcClient;
2863 GetClientRect(rcClient);
2864 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2865 int marginwidth = MARGINWIDTH;
2866 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2868 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2870 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2872 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2874 // inside the header part of the view (showing the filename)
2875 pTI->hwnd = this->m_hWnd;
2876 this->GetClientRect(&pTI->rect);
2877 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2878 pTI->uId = (UINT)m_hWnd;
2879 pTI->lpszText = LPSTR_TEXTCALLBACK;
2881 // we want multi line tooltips
2882 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2883 if (pToolTip->GetSafeHwnd() != NULL)
2885 pToolTip->SetMaxTipWidth(INT_MAX);
2888 return (textrect.PtInRect(point) ? 1 : 2);
2891 return -1;
2894 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2896 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2897 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2899 switch (nChar)
2901 case VK_TAB:
2902 if (bControl)
2904 if (this==m_pwndLeft)
2906 if (IsViewGood(m_pwndRight))
2908 m_pwndRight->SetFocus();
2910 else if (IsViewGood(m_pwndBottom))
2912 m_pwndBottom->SetFocus();
2915 else if (this==m_pwndRight)
2917 if (IsViewGood(m_pwndBottom))
2919 m_pwndBottom->SetFocus();
2921 else if (IsViewGood(m_pwndLeft))
2923 m_pwndLeft->SetFocus();
2926 else if (this==m_pwndBottom)
2928 if (IsViewGood(m_pwndLeft))
2930 m_pwndLeft->SetFocus();
2932 else if (IsViewGood(m_pwndRight))
2934 m_pwndRight->SetFocus();
2938 break;
2939 case VK_PRIOR:
2941 POINT ptCaretPos = GetCaretPosition();
2942 ptCaretPos.y -= GetScreenLines();
2943 ptCaretPos.y = max(ptCaretPos.y, 0);
2944 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2945 SetCaretPosition(ptCaretPos);
2946 OnCaretMove(MOVELEFT, bShift);
2947 ShowDiffLines(ptCaretPos.y);
2949 break;
2950 case VK_NEXT:
2952 POINT ptCaretPos = GetCaretPosition();
2953 ptCaretPos.y += GetScreenLines();
2954 if (ptCaretPos.y >= GetLineCount())
2955 ptCaretPos.y = GetLineCount()-1;
2956 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2957 SetCaretPosition(ptCaretPos);
2958 OnCaretMove(MOVERIGHT, bShift);
2959 ShowDiffLines(ptCaretPos.y);
2961 break;
2962 case VK_HOME:
2964 if (bControl)
2966 ScrollAllToLine(0);
2967 SetCaretToViewStart();
2968 m_nCaretGoalPos = 0;
2969 if (bShift)
2970 AdjustSelection(MOVELEFT);
2971 else
2972 ClearSelection();
2973 UpdateCaret();
2975 else
2977 POINT ptCaretPos = GetCaretPosition();
2978 CString sLine = GetLineChars(ptCaretPos.y);
2979 int pos = 0;
2980 while (pos < sLine.GetLength())
2982 if (sLine[pos] != ' ' && sLine[pos] != '\t')
2983 break;
2984 ++pos;
2986 if (ptCaretPos.x == pos)
2988 SetCaretToLineStart();
2989 m_nCaretGoalPos = 0;
2990 OnCaretMove(MOVERIGHT, bShift);
2991 ScrollAllToChar(0);
2993 else
2995 ptCaretPos.x = pos;
2996 SetCaretAndGoalPosition(ptCaretPos);
2997 OnCaretMove(bShift);
3001 break;
3002 case VK_END:
3004 if (bControl)
3006 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3007 POINT ptCaretPos;
3008 ptCaretPos.y = GetLineCount()-1;
3009 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3010 SetCaretAndGoalPosition(ptCaretPos);
3011 if (bShift)
3012 AdjustSelection(MOVERIGHT);
3013 else
3014 ClearSelection();
3016 else
3018 POINT ptCaretPos = GetCaretPosition();
3019 ptCaretPos.x = GetLineLength(ptCaretPos.y);
3020 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3022 ptCaretPos.x--;
3024 SetCaretAndGoalPosition(ptCaretPos);
3025 OnCaretMove(bShift);
3028 break;
3029 case VK_BACK:
3030 if (IsWritable())
3032 if (! HasTextSelection())
3034 POINT ptCaretPos = GetCaretPosition();
3035 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3036 break;
3037 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3038 if (bControl)
3039 MoveCaretWordLeft();
3040 else
3042 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3046 m_ptSelectionViewPosStart = GetCaretViewPosition();
3048 RemoveSelectedText();
3050 break;
3051 case VK_DELETE:
3052 if (IsWritable())
3054 if (! HasTextSelection())
3056 if (bControl)
3058 m_ptSelectionViewPosStart = GetCaretViewPosition();
3059 MoveCaretWordRight();
3060 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3062 else
3064 if (! MoveCaretRight())
3065 break;
3066 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3067 MoveCaretLeft();
3068 m_ptSelectionViewPosStart = GetCaretViewPosition();
3071 RemoveSelectedText();
3073 break;
3074 case VK_INSERT:
3075 m_bInsertMode = !m_bInsertMode;
3076 UpdateCaret();
3077 break;
3079 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3082 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3084 const int nClickedLine = GetButtonEventLineIndex(point);
3085 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3087 POINT ptCaretPos;
3088 ptCaretPos.y = nClickedLine;
3089 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3090 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3091 SetCaretAndGoalPosition(ptCaretPos);
3093 if (nFlags & MK_SHIFT)
3094 AdjustSelection(MOVERIGHT);
3095 else
3097 ClearSelection();
3098 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3099 if (point.x < GetMarginWidth())
3101 // select the whole line
3102 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3103 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3107 UpdateViewsCaretPosition();
3108 Invalidate();
3111 CView::OnLButtonDown(nFlags, point);
3114 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3116 if (zChar == ' ' || zChar == '\t' )
3118 return CHG_WHITESPACE;
3120 if (zChar < 0x20)
3122 return CHG_CONTROL;
3124 if (m_sWordSeparators.Find(zChar) >= 0)
3126 return CHG_WORDSEPARATOR;
3128 return CHG_WORDLETTER;
3131 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3133 if (m_pViewData == 0) {
3134 CView::OnLButtonDblClk(nFlags, point);
3135 return;
3138 const int nClickedLine = GetButtonEventLineIndex(point);
3139 if ( nClickedLine < 0)
3140 return;
3141 int nViewLine = GetViewLineForScreen(nClickedLine);
3142 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3144 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3146 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3148 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3149 int screenLine = FindViewLineNumber(movedindex);
3150 int nTop = screenLine - GetScreenLines()/2;
3151 if (nTop < 0)
3152 nTop = 0;
3153 ScrollAllToLine(nTop);
3154 // find and select the whole moved block
3155 int startSel = movedindex;
3156 int endSel = movedindex;
3157 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3158 startSel--;
3159 startSel++;
3160 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3161 endSel++;
3162 endSel--;
3163 m_pOtherView->SetupSelection(startSel, endSel);
3164 return CView::OnLButtonDblClk(nFlags, point);
3168 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3170 // a double click on a marker expands the hidden text
3171 int i = nViewLine;
3172 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3174 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3175 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3176 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3177 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3178 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3179 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3180 i++;
3182 BuildAllScreen2ViewVector();
3183 if (m_pwndLeft)
3184 m_pwndLeft->Invalidate();
3185 if (m_pwndRight)
3186 m_pwndRight->Invalidate();
3187 if (m_pwndBottom)
3188 m_pwndBottom->Invalidate();
3190 else
3192 POINT ptCaretPos;
3193 ptCaretPos.y = nClickedLine;
3194 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3195 SetCaretPosition(ptCaretPos);
3196 ClearSelection();
3198 POINT ptViewCarret = GetCaretViewPosition();
3199 nViewLine = ptViewCarret.y;
3200 if (nViewLine >= GetViewCount())
3201 return;
3202 const CString &sLine = GetViewLine(nViewLine);
3203 int nLineLength = sLine.GetLength();
3204 int nBasePos = ptViewCarret.x;
3205 // get target char group
3206 ECharGroup eLeft = CHG_UNKNOWN;
3207 if (nBasePos > 0)
3209 eLeft = GetCharGroup(sLine[nBasePos-1]);
3211 ECharGroup eRight = CHG_UNKNOWN;
3212 if (nBasePos < nLineLength)
3214 eRight = GetCharGroup(sLine[nBasePos]);
3216 ECharGroup eTarget = max(eRight, eLeft);
3217 // find left margin
3218 int nLeft = nBasePos;
3219 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3221 nLeft--;
3223 // get right margin
3224 int nRight = nBasePos;
3225 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3227 nRight++;
3229 // set selection
3230 m_ptSelectionViewPosStart.x = nLeft;
3231 m_ptSelectionViewPosStart.y = nViewLine;
3232 m_ptSelectionViewPosEnd.x = nRight;
3233 m_ptSelectionViewPosEnd.y = nViewLine;
3234 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3235 SetupAllViewSelection(nViewLine, nViewLine);
3236 // set caret
3237 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3238 UpdateViewsCaretPosition();
3239 UpdateGoalPos();
3241 // set mark word
3242 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3243 int nMarkWidth = max(nRight - nLeft, 0);
3244 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3245 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3247 m_sMarkedWord.Empty();
3250 if (m_pwndLeft)
3251 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3252 if (m_pwndRight)
3253 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3254 if (m_pwndBottom)
3255 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3257 Invalidate();
3258 if (m_pwndLocator)
3259 m_pwndLocator->Invalidate();
3262 CView::OnLButtonDblClk(nFlags, point);
3265 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3267 const int nClickedLine = GetButtonEventLineIndex(point);
3268 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3270 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3272 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3273 if (pidl)
3275 SHOpenFolderAndSelectItems(pidl,0,0,0);
3276 CoTaskMemFree((LPVOID)pidl);
3279 return;
3281 POINT ptCaretPos;
3282 ptCaretPos.y = nClickedLine;
3283 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3284 SetCaretAndGoalPosition(ptCaretPos);
3285 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3286 if (m_pwndLeft)
3287 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3288 if (m_pwndRight)
3289 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3290 if (m_pwndBottom)
3291 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3292 ClearSelection();
3293 m_ptSelectionViewPosStart.x = 0;
3294 m_ptSelectionViewPosStart.y = nClickedLine;
3295 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3296 m_ptSelectionViewPosEnd.y = nClickedLine;
3297 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3298 UpdateViewsCaretPosition();
3299 Invalidate();
3300 if (m_pwndLocator)
3301 m_pwndLocator->Invalidate();
3304 void CBaseView::OnEditCopy()
3306 CString sCopyData = GetSelectedText();
3308 if (!sCopyData.IsEmpty())
3310 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3314 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3316 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3318 --m_pMainFrame->m_nMoveMovesToIgnore;
3319 CView::OnMouseMove(nFlags, point);
3320 return;
3322 int nMouseLine = GetButtonEventLineIndex(point);
3323 if (nMouseLine < -1)
3324 nMouseLine = -1;
3325 m_mouseInMargin = point.x < GetMarginWidth();
3327 ShowDiffLines(nMouseLine);
3329 KillTimer(IDT_SCROLLTIMER);
3330 if (nFlags & MK_LBUTTON)
3332 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3333 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3334 if (saveMouseLine < 0)
3335 return;
3336 int col = CalcColFromPoint(point.x, saveMouseLine);
3337 int charIndex = CalculateCharIndex(saveMouseLine, col);
3338 if (HasSelection() &&
3339 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3341 POINT ptCaretPos = {charIndex, nMouseLine};
3342 SetCaretAndGoalPosition(ptCaretPos);
3343 AdjustSelection(MOVERIGHT);
3344 Invalidate();
3345 UpdateWindow();
3347 if (nMouseLine < m_nTopLine)
3349 ScrollAllToLine(m_nTopLine-1, TRUE);
3350 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3352 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3354 ScrollAllToLine(m_nTopLine+1, TRUE);
3355 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3357 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3359 ScrollAllSide(-1);
3360 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3362 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3364 ScrollAllSide(1);
3365 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3367 SetCapture();
3371 CView::OnMouseMove(nFlags, point);
3374 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3376 ShowDiffLines(-1);
3377 ReleaseCapture();
3378 KillTimer(IDT_SCROLLTIMER);
3380 __super::OnLButtonUp(nFlags, point);
3383 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3385 if (nIDEvent == IDT_SCROLLTIMER)
3387 POINT point;
3388 GetCursorPos(&point);
3389 ScreenToClient(&point);
3390 int nMouseLine = GetButtonEventLineIndex(point);
3391 if (nMouseLine < -1)
3393 nMouseLine = -1;
3395 if (GetKeyState(VK_LBUTTON)&0x8000)
3397 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3398 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3399 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3400 if (nMouseLine < m_nTopLine)
3402 ScrollAllToLine(m_nTopLine-1, TRUE);
3403 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3405 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3407 ScrollAllToLine(m_nTopLine+1, TRUE);
3408 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3410 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3412 ScrollAllSide(-1);
3413 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3415 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3417 ScrollAllSide(1);
3418 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3424 CView::OnTimer(nIDEvent);
3427 void CBaseView::ShowDiffLines(int nLine)
3429 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3431 m_pwndLineDiffBar->ShowLines(nLine);
3432 nLine = -1;
3433 m_nMouseLine = nLine;
3434 return;
3437 if ((!m_pwndRight)||(!m_pwndLeft))
3438 return;
3439 if(m_pMainFrame->m_bOneWay)
3440 return;
3442 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3443 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3445 if (nLine < 0)
3446 return;
3448 if (nLine != m_nMouseLine)
3450 if (nLine >= GetLineCount())
3451 nLine = -1;
3452 m_nMouseLine = nLine;
3453 m_pwndLineDiffBar->ShowLines(nLine);
3455 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3458 const viewdata& CBaseView::GetEmptyLineData()
3460 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3461 return emptyLine;
3464 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3466 for (int i = 0; i < nCount; i++)
3468 InsertViewData(nFirstView, GetEmptyLineData());
3473 void CBaseView::UpdateCaret()
3475 POINT ptCaretPos = GetCaretPosition();
3476 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3477 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3478 SetCaretPosition(ptCaretPos);
3480 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3482 if (m_bFocused &&
3483 ptCaretPos.y >= m_nTopLine &&
3484 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3485 nCaretOffset >= m_nOffsetChar &&
3486 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3488 POINT pt1 = TextToClient(ptCaretPos);
3489 if (m_bInsertMode)
3490 CreateSolidCaret(2, GetLineHeight());
3491 else
3493 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3494 POINT pt2 = TextToClient(pt);
3495 int width = max(GetCharWidth(), pt2.x - pt1.x);
3496 CreateSolidCaret(width, GetLineHeight());
3498 SetCaretPos(pt1);
3499 ShowCaret();
3501 else
3503 HideCaret();
3507 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3509 POINT ptViewPos;
3510 ptViewPos.x = pt.x;
3512 int nSubLine = GetSubLineOffset(pt.y);
3513 if (nSubLine > 0)
3515 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3517 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3521 ptViewPos.y = GetViewLineForScreen(pt.y);
3522 return ptViewPos;
3525 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3527 POINT ptPos;
3528 int nViewLineLenLeft = GetViewLineLength(pt.y);
3529 ptPos.x = min(nViewLineLenLeft, pt.x);
3530 ptPos.y = FindScreenLineForViewLine(pt.y);
3531 if (GetViewLineForScreen(ptPos.y) != pt.y )
3533 ptPos.x = 0;
3535 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3537 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3538 while (nSubLineLength < ptPos.x)
3540 ptPos.x -= nSubLineLength;
3541 nViewLineLenLeft -= nSubLineLength;
3542 ptPos.y++;
3543 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3545 // last pos of non last sub-line go to start of next screen line
3546 // Note: while this works correctly, it's not what a user might expect:
3547 // cursor-right when the caret is before the last char of a wrapped line
3548 // now moves the caret to the next line. But users expect the caret to
3549 // move to the right of the last char instead, and with another cursor-right
3550 // keystroke to move the caret to the next line.
3551 // Basically, this would require to handle two caret positions for the same
3552 // logical position in the line string (one on the last position of the first line,
3553 // one on the first position of the new line. For non-wrapped lines this works
3554 // because there's an 'invisible' newline char at the end of the first line.
3555 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3557 ptPos.x = 0;
3558 ptPos.y++;
3562 return ptPos;
3566 void CBaseView::EnsureCaretVisible()
3568 POINT ptCaretPos = GetCaretPosition();
3569 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3571 if (ptCaretPos.y < m_nTopLine)
3572 ScrollAllToLine(ptCaretPos.y);
3573 int screnLines = GetScreenLines();
3574 if (screnLines)
3576 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3577 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3578 if (nCaretOffset < m_nOffsetChar)
3579 ScrollAllToChar(nCaretOffset);
3580 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3581 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3585 int CBaseView::CalculateActualOffset(const POINT& point)
3587 int nLineIndex = point.y;
3588 int nCharIndex = point.x;
3589 ASSERT(nCharIndex >= 0);
3590 CString sLine = GetLineChars(nLineIndex);
3591 int nLineLength = sLine.GetLength();
3592 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3595 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3597 int nLength = GetLineLength(nLineIndex);
3598 int nSubLine = GetSubLineOffset(nLineIndex);
3599 if (nSubLine>=0)
3601 int nViewLine = GetViewLineForScreen(nLineIndex);
3602 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3604 int nMultilineCount = CountMultiLines(nViewLine);
3605 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3607 nLength--;
3611 CString Line = GetLineChars(nLineIndex);
3612 int nIndex = 0;
3613 int nOffset = 0;
3614 int nTabSize = GetTabSize();
3615 while (nOffset < nActualOffset && nIndex < nLength)
3617 if (Line.GetAt(nIndex) == _T('\t'))
3618 nOffset += (nTabSize - nOffset % nTabSize);
3619 else
3620 ++nOffset;
3621 ++nIndex;
3623 return nIndex;
3627 * @param xpos X coordinate in CBaseView
3628 * @param lineIndex logical line index (e.g. wrap/collapse)
3630 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3632 int xpos2;
3633 CDC *pDC = GetDC();
3634 if (pDC != nullptr)
3636 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3637 int fit = text.GetLength();
3638 std::unique_ptr<int> posBuffer(new int[fit]);
3639 pDC->SelectObject(GetFont()); // is this right font ?
3640 SIZE size;
3641 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3642 ReleaseDC(pDC);
3643 int lower = -1, upper = fit - 1;
3644 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3647 int middle = (upper + lower + 1) / 2;
3648 int width = posBuffer.get()[middle];
3649 if (xcheck < width)
3650 upper = middle - 1;
3651 else
3652 lower = middle;
3653 } while (lower < upper);
3654 lower++;
3655 xpos2 = lower;
3656 if (lower < fit - 1)
3658 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3659 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3660 xpos2++;
3663 else
3665 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3666 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3667 xpos2++;
3669 return xpos2;
3672 POINT CBaseView::TextToClient(const POINT& point)
3674 POINT pt;
3675 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3676 pt.y = nOffsetScreenLine * GetLineHeight();
3677 pt.x = CalculateActualOffset(point);
3679 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3680 CDC * pDC = GetDC();
3681 if (pDC)
3683 pDC->SelectObject(GetFont()); // is this right font ?
3684 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3685 CString sLine = GetLineChars(nScreenLine);
3686 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3687 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3688 ReleaseDC(pDC);
3689 } else {
3690 nLeft += pt.x * GetCharWidth();
3693 pt.x = nLeft;
3694 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3695 return pt;
3698 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3700 CView::OnChar(nChar, nRepCnt, nFlags);
3702 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3703 bool bSkipSelectionClear = false;
3705 if (IsReadonly())
3706 return;
3708 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3709 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3711 return;
3714 if (!m_pViewData) // no data - nothing to do
3715 return;
3717 if (nChar == VK_F16)
3719 // generated by a ctrl+backspace - ignore.
3721 else if (nChar==VK_TAB && HasTextLineSelection())
3723 // change indentation for selected lines
3724 if (bShift)
3726 RemoveIndentationForSelectedBlock();
3728 else
3730 AddIndentationForSelectedBlock();
3732 bSkipSelectionClear = true;
3734 else if ((nChar > 31)||(nChar == VK_TAB))
3736 ResetUndoStep();
3737 RemoveSelectedText();
3738 POINT ptCaretViewPos = GetCaretViewPosition();
3739 int nViewLine = ptCaretViewPos.y;
3740 if ((nViewLine==0)&&(GetViewCount()==0))
3741 OnChar(VK_RETURN, 0, 0);
3742 int charCount = 1;
3743 viewdata lineData = GetViewData(nViewLine);
3744 if (nChar == VK_TAB)
3746 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3747 if (indentChars > 0)
3749 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3750 charCount = indentChars;
3752 else
3753 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3755 else
3757 if (m_bInsertMode)
3758 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3759 else
3761 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3762 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3763 else
3764 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3767 if (IsStateEmpty(lineData.state))
3769 // if not last line set EOL
3770 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3772 if (!IsViewLineEmpty(nCheckViewLine))
3774 lineData.ending = m_lineendings;
3775 break;
3778 // make sure previous (non empty) line have EOL set
3779 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3781 if (!IsViewLineEmpty(nCheckViewLine))
3783 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3785 SetViewLineEnding(nCheckViewLine, m_lineendings);
3787 break;
3791 lineData.state = DIFFSTATE_EDITED;
3792 bool bNeedRenumber = false;
3793 if (lineData.linenumber == -1)
3795 lineData.linenumber = 0;
3796 bNeedRenumber = true;
3798 SetViewData(nViewLine, lineData);
3799 SetModified();
3800 SaveUndoStep();
3801 BuildAllScreen2ViewVector(nViewLine);
3802 if (bNeedRenumber)
3804 UpdateViewLineNumbers();
3806 for (int i = 0; i < charCount; ++i)
3807 MoveCaretRight();
3808 UpdateGoalPos();
3810 else if (nChar == 10)
3812 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3813 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3814 EOL newEOL = EOL_CRLF;
3815 switch (eol)
3817 case EOL_CRLF:
3818 newEOL = EOL_CR;
3819 break;
3820 case EOL_CR:
3821 newEOL = EOL_LF;
3822 break;
3823 case EOL_LF:
3824 newEOL = EOL_CRLF;
3825 break;
3827 if (eol==EOL_NOENDING || eol==newEOL)
3828 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3829 // to add EOL on newly edited empty line hit enter
3830 // don't store into UNDO if no change happened
3831 // and don't mark file as modified
3832 return;
3833 AddUndoViewLine(nViewLine);
3834 m_pViewData->SetLineEnding(nViewLine, newEOL);
3835 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3836 UpdateGoalPos();
3838 else if (nChar == VK_RETURN)
3840 // insert a new, fresh and empty line below the cursor
3841 RemoveSelectedText();
3843 CUndo::GetInstance().BeginGrouping();
3845 POINT ptCaretViewPos = GetCaretViewPosition();
3846 int nViewLine = ptCaretViewPos.y;
3847 int nLeft = ptCaretViewPos.x;
3848 CString sLine = GetViewLineChars(nViewLine);
3849 CString sLineLeft = sLine.Left(nLeft);
3850 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3851 EOL eOriginalEnding = EOL_AUTOLINE;
3852 if (m_pViewData->GetCount() > nViewLine)
3853 eOriginalEnding = GetViewLineEnding(nViewLine);
3855 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3857 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3858 SetViewData(nViewLine, newFirstLine);
3861 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3862 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3863 InsertViewData(nInsertLine, newLastLine);
3864 SetModified();
3865 SaveUndoStep();
3867 // adds new line everywhere except me
3868 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3870 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3872 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3874 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3876 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3878 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3880 SaveUndoStep();
3882 UpdateViewLineNumbers();
3883 SaveUndoStep();
3884 CUndo::GetInstance().EndGrouping();
3886 BuildAllScreen2ViewVector();
3887 // move the cursor to the new line
3888 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3889 SetCaretAndGoalViewPosition(ptCaretViewPos);
3891 else
3892 return; // Unknown control character -- ignore it.
3893 if (!bSkipSelectionClear)
3894 ClearSelection();
3895 EnsureCaretVisible();
3896 UpdateCaret();
3897 Invalidate(FALSE);
3900 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3902 ResetUndoStep();
3903 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3904 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3905 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3906 SetModified();
3907 SaveUndoStep();
3908 RecalcAllVertScrollBars();
3909 Invalidate(FALSE);
3912 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3914 if (m_pViewData == NULL)
3915 return;
3916 int viewLine = nViewLineIndex;
3917 EOL ending = m_pViewData->GetLineEnding(viewLine);
3918 if (ending == EOL_NOENDING)
3920 ending = m_lineendings;
3922 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3923 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3925 CString sPartLine = GetViewLineChars(nViewLineIndex);
3926 int nPosx = GetCaretPosition().x; // should be view pos ?
3927 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3928 sPartLine = sPartLine.Mid(nPosx);
3929 newLine.sLine = sPartLine;
3931 m_pViewData->InsertData(viewLine+1, newLine);
3932 BuildAllScreen2ViewVector();
3935 void CBaseView::RemoveSelectedText()
3937 if (m_pViewData == NULL)
3938 return;
3939 if (!HasTextSelection())
3940 return;
3942 // fix selection if starts or ends on empty line
3943 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3944 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3947 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3948 SetCaretViewPosition(m_ptSelectionViewPosStart);
3949 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3952 m_ptSelectionViewPosStart = GetCaretViewPosition();
3953 if (!HasTextSelection())
3955 ClearSelection();
3956 return;
3959 // We want to undo the insertion in a single step.
3960 ResetUndoStep();
3961 CUndo::GetInstance().BeginGrouping();
3963 // combine first and last line
3964 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3965 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3966 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3967 oFirstLine.ending = oLastLine.ending;
3968 oFirstLine.state = DIFFSTATE_EDITED;
3969 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3971 // clean up middle lines if any
3972 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3974 viewdata oEmptyLine = GetEmptyLineData();
3975 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3977 SetViewData(nViewLine, oEmptyLine);
3979 SaveUndoStep();
3981 if (CleanEmptyLines())
3983 BuildAllScreen2ViewVector(); // schedule full rebuild
3985 SaveUndoStep();
3986 UpdateViewLineNumbers();
3989 SetModified(); //TODO set modified only if real data was changed
3990 SaveUndoStep();
3991 CUndo::GetInstance().EndGrouping();
3993 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3994 SetCaretViewPosition(m_ptSelectionViewPosStart);
3995 UpdateGoalPos();
3996 ClearSelection();
3997 UpdateCaret();
3998 EnsureCaretVisible();
3999 Invalidate(FALSE);
4002 void CBaseView::PasteText()
4004 if (!OpenClipboard())
4005 return;
4007 CString sClipboardText;
4008 HGLOBAL hglb = GetClipboardData(CF_TEXT);
4009 if (hglb)
4011 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
4012 sClipboardText = CString(lpstr);
4013 GlobalUnlock(hglb);
4015 hglb = GetClipboardData(CF_UNICODETEXT);
4016 if (hglb)
4018 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
4019 sClipboardText = lpstr;
4020 GlobalUnlock(hglb);
4022 CloseClipboard();
4024 if (sClipboardText.IsEmpty())
4025 return;
4027 sClipboardText.Replace(_T("\r\n"), _T("\r"));
4028 sClipboardText.Replace('\n', '\r');
4030 InsertText(sClipboardText);
4033 void CBaseView::OnCaretDown()
4035 POINT ptCaretPos = GetCaretPosition();
4036 int nLine = ptCaretPos.y;
4037 int nNextLine = nLine + 1;
4038 if (nNextLine >= GetLineCount()) // already at last line
4040 return;
4043 POINT ptCaretViewPos = GetCaretViewPosition();
4044 int nViewLine = ptCaretViewPos.y;
4045 int nNextViewLine = GetViewLineForScreen(nNextLine);
4046 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4048 // find next suitable screen line
4049 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4051 nNextLine++;
4052 if (nNextLine >= GetLineCount())
4054 return;
4056 nNextViewLine = GetViewLineForScreen(nNextLine);
4059 ptCaretPos.y = nNextLine;
4060 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4061 SetCaretPosition(ptCaretPos);
4062 OnCaretMove(MOVELEFT);
4063 ShowDiffLines(ptCaretPos.y);
4066 bool CBaseView::MoveCaretLeft()
4068 POINT ptCaretViewPos = GetCaretViewPosition();
4070 //int nViewLine = ptCaretViewPos.y;
4071 if (ptCaretViewPos.x == 0)
4073 int nPrevLine = GetCaretPosition().y;
4074 int nPrevViewLine;
4075 do {
4076 nPrevLine--;
4077 if (nPrevLine < 0)
4079 return false;
4081 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4082 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4083 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4084 ShowDiffLines(nPrevLine);
4086 else
4087 --ptCaretViewPos.x;
4089 SetCaretAndGoalViewPosition(ptCaretViewPos);
4090 return true;
4093 bool CBaseView::MoveCaretRight()
4095 POINT ptCaretViewPos = GetCaretViewPosition();
4097 int nViewLine = ptCaretViewPos.y;
4098 int nViewLineLen = GetViewLineLength(nViewLine);
4099 if (ptCaretViewPos.x >= nViewLineLen)
4101 int nNextLine = GetCaretPosition().y;
4102 int nNextViewLine;
4103 do {
4104 nNextLine++;
4105 if (nNextLine >= GetLineCount())
4107 return false;
4109 nNextViewLine = GetViewLineForScreen(nNextLine);
4110 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4111 ptCaretViewPos.y = nNextViewLine;
4112 ptCaretViewPos.x = 0;
4113 ShowDiffLines(nNextLine);
4115 else
4116 ++ptCaretViewPos.x;
4118 SetCaretAndGoalViewPosition(ptCaretViewPos);
4119 return true;
4122 void CBaseView::UpdateGoalPos()
4124 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4127 void CBaseView::OnCaretLeft()
4129 MoveCaretLeft();
4130 OnCaretMove(MOVELEFT);
4133 void CBaseView::OnCaretRight()
4135 MoveCaretRight();
4136 OnCaretMove(MOVERIGHT);
4139 void CBaseView::OnCaretUp()
4141 POINT ptCaretPos = GetCaretPosition();
4142 int nLine = ptCaretPos.y;
4143 if (nLine <= 0) // already at first line
4145 return;
4147 int nPrevLine = nLine - 1;
4149 POINT ptCaretViewPos = GetCaretViewPosition();
4150 int nViewLine = ptCaretViewPos.y;
4151 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4152 if (nPrevViewLine != nViewLine) // not on same view line
4154 // find previous suitable screen line
4155 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4157 if (nPrevLine <= 0)
4159 return;
4161 nPrevLine--;
4162 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4165 ptCaretPos.y = nPrevLine;
4166 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4167 SetCaretPosition(ptCaretPos);
4168 OnCaretMove(MOVELEFT);
4169 ShowDiffLines(ptCaretPos.y);
4172 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4174 switch (GetCharGroup(ch))
4176 case CHG_CONTROL:
4177 case CHG_WHITESPACE:
4178 case CHG_WORDSEPARATOR:
4179 return true;
4181 return false;
4184 bool CBaseView::IsCaretAtWordBoundary()
4186 POINT ptViewCaret = GetCaretViewPosition();
4187 CString line = GetViewLineChars(ptViewCaret.y);
4188 if (line.IsEmpty())
4189 return false; // no boundary at the empty lines
4190 if (ptViewCaret.x == 0)
4191 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4192 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4193 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4194 return
4195 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4196 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4199 void CBaseView::UpdateViewsCaretPosition()
4201 POINT ptCaretPos = GetCaretPosition();
4202 if (m_pwndBottom && m_pwndBottom!=this)
4203 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4204 if (m_pwndLeft && m_pwndLeft!=this)
4205 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4206 if (m_pwndRight && m_pwndRight!=this)
4207 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4210 void CBaseView::OnCaretWordleft()
4212 MoveCaretWordLeft();
4213 OnCaretMove(MOVELEFT);
4216 void CBaseView::OnCaretWordright()
4218 MoveCaretWordRight();
4219 OnCaretMove(MOVERIGHT);
4222 void CBaseView::MoveCaretWordLeft()
4224 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4229 void CBaseView::MoveCaretWordRight()
4231 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4236 void CBaseView::ClearCurrentSelection()
4238 m_ptSelectionViewPosStart = GetCaretViewPosition();
4239 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4240 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4241 m_nSelViewBlockStart = -1;
4242 m_nSelViewBlockEnd = -1;
4243 Invalidate(FALSE);
4246 void CBaseView::ClearSelection()
4248 if (m_pwndLeft)
4249 m_pwndLeft->ClearCurrentSelection();
4250 if (m_pwndRight)
4251 m_pwndRight->ClearCurrentSelection();
4252 if (m_pwndBottom)
4253 m_pwndBottom->ClearCurrentSelection();
4256 void CBaseView::AdjustSelection(bool bMoveLeft)
4258 POINT ptCaretViewPos = GetCaretViewPosition();
4259 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4261 // select all have been used recently update origin
4262 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4264 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4265 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4267 m_ptSelectionViewPosStart = ptCaretViewPos;
4268 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4270 else
4272 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4273 m_ptSelectionViewPosEnd = ptCaretViewPos;
4276 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4278 Invalidate(FALSE);
4281 void CBaseView::OnEditCut()
4283 if (IsWritable())
4285 OnEditCopy();
4286 RemoveSelectedText();
4290 void CBaseView::OnEditPaste()
4292 if (IsWritable())
4294 CUndo::GetInstance().BeginGrouping();
4295 RemoveSelectedText();
4296 PasteText();
4297 CUndo::GetInstance().EndGrouping();
4301 void CBaseView::DeleteFonts()
4303 for (int i=0; i<fontsCount; i++)
4305 if (m_apFonts[i] != NULL)
4307 m_apFonts[i]->DeleteObject();
4308 delete m_apFonts[i];
4309 m_apFonts[i] = NULL;
4314 void CBaseView::OnCaretMove(bool bMoveLeft)
4316 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4317 OnCaretMove(bMoveLeft, bShift);
4320 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4322 if(isShiftPressed)
4323 AdjustSelection(bMoveLeft);
4324 else
4325 ClearSelection();
4326 EnsureCaretVisible();
4327 UpdateCaret();
4330 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4332 AddCutCopyAndPaste(popup);
4335 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4337 popup.AppendMenu(MF_SEPARATOR, NULL);
4338 CString temp;
4339 temp.LoadString(IDS_EDIT_COPY);
4340 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4341 if (IsWritable())
4343 temp.LoadString(IDS_EDIT_CUT);
4344 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4345 temp.LoadString(IDS_EDIT_PASTE);
4346 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4347 popup.AppendMenu(MF_SEPARATOR, NULL);
4351 void CBaseView::CompensateForKeyboard(CPoint& point)
4353 // if the context menu is invoked through the keyboard, we have to use
4354 // a calculated position on where to anchor the menu on
4355 if (ArePointsSame(point, SetupPoint(-1, -1)))
4357 CRect rect;
4358 GetWindowRect(&rect);
4359 point = rect.CenterPoint();
4363 HICON CBaseView::LoadIcon(WORD iconId)
4365 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4366 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4367 return (HICON)icon;
4370 void CBaseView::ReleaseBitmap()
4372 if (m_pCacheBitmap != NULL)
4374 m_pCacheBitmap->DeleteObject();
4375 delete m_pCacheBitmap;
4376 m_pCacheBitmap = NULL;
4380 void CBaseView::BuildMarkedWordArray()
4382 int lineCount = GetLineCount();
4383 m_arMarkedWordLines.clear();
4384 m_arMarkedWordLines.reserve(lineCount);
4385 bool bDoit = !m_sMarkedWord.IsEmpty();
4386 for (int i = 0; i < lineCount; ++i)
4388 if (bDoit)
4390 CString line = GetLineChars(i);
4392 if (!line.IsEmpty())
4394 int found = 0;
4395 int nMarkStart = -1;
4396 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4398 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4399 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4400 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4401 if (eLeft != eStart)
4403 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4404 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4405 if (eRight != eEnd)
4407 found = 1;
4408 break;
4412 m_arMarkedWordLines.push_back(found);
4414 else
4415 m_arMarkedWordLines.push_back(0);
4417 else
4418 m_arMarkedWordLines.push_back(0);
4422 void CBaseView::BuildFindStringArray()
4424 int lineCount = GetLineCount();
4425 m_arFindStringLines.clear();
4426 m_arFindStringLines.reserve(lineCount);
4427 bool bDoit = !m_sFindText.IsEmpty();
4428 int s = 0;
4429 int e = 0;
4430 for (int i = 0; i < lineCount; ++i)
4432 if (bDoit)
4434 CString line = GetLineChars(i);
4436 if (!line.IsEmpty())
4438 switch (m_pViewData->GetState(GetViewLineForScreen(i)))
4440 case DIFFSTATE_EMPTY:
4441 m_arFindStringLines.push_back(0);
4442 break;
4443 case DIFFSTATE_UNKNOWN:
4444 case DIFFSTATE_NORMAL:
4445 if (m_bLimitToDiff)
4447 m_arFindStringLines.push_back(0);
4448 break;
4450 case DIFFSTATE_REMOVED:
4451 case DIFFSTATE_REMOVEDWHITESPACE:
4452 case DIFFSTATE_ADDED:
4453 case DIFFSTATE_ADDEDWHITESPACE:
4454 case DIFFSTATE_WHITESPACE:
4455 case DIFFSTATE_WHITESPACE_DIFF:
4456 case DIFFSTATE_CONFLICTED:
4457 case DIFFSTATE_CONFLICTED_IGNORED:
4458 case DIFFSTATE_CONFLICTADDED:
4459 case DIFFSTATE_CONFLICTEMPTY:
4460 case DIFFSTATE_CONFLICTRESOLVED:
4461 case DIFFSTATE_IDENTICALREMOVED:
4462 case DIFFSTATE_IDENTICALADDED:
4463 case DIFFSTATE_THEIRSREMOVED:
4464 case DIFFSTATE_THEIRSADDED:
4465 case DIFFSTATE_YOURSREMOVED:
4466 case DIFFSTATE_YOURSADDED:
4467 case DIFFSTATE_EDITED:
4469 if (!m_bMatchCase)
4470 line = line.MakeLower();
4471 s = 0;
4472 e = 0;
4473 int match = 0;
4474 while (StringFound(line, SearchNext, s, e))
4476 match++;
4477 s = e;
4478 e = 0;
4480 m_arFindStringLines.push_back(match);
4481 break;
4483 default:
4484 m_arFindStringLines.push_back(0);
4487 else
4488 m_arFindStringLines.push_back(0);
4490 else
4491 m_arFindStringLines.push_back(0);
4493 UpdateLocator();
4496 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4498 if (!m_bShowInlineDiff)
4499 return false;
4500 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4501 return false;
4503 if (m_pViewData == nullptr || m_pViewData->GetCount() <= nViewLine)
4504 return false;
4505 const CString &sLine = m_pViewData->GetLine(nViewLine);
4506 if (sLine.IsEmpty())
4507 return false;
4509 CheckOtherView();
4510 if (!m_pOtherViewData)
4511 return false;
4513 const CString &sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4514 if (sDiffLine.IsEmpty())
4515 return false;
4517 svn_diff_t * diff = NULL;
4518 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4519 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4520 return false;
4522 size_t lineoffset = 0;
4523 size_t position = 0;
4524 while (diff)
4526 apr_off_t len = diff->original_length;
4527 size_t oldpos = position;
4529 for (apr_off_t i = 0; i < len; ++i)
4531 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4532 lineoffset++;
4535 if (diff->type == svn_diff__type_diff_modified)
4537 inlineDiffPos p;
4538 p.start = oldpos;
4539 p.end = position;
4540 positions.push_back(p);
4543 diff = diff->next;
4546 return !positions.empty();
4549 void CBaseView::OnNavigateNextinlinediff()
4551 int nX;
4552 if (GetNextInlineDiff(nX))
4554 POINT ptCaretViewPos = GetCaretViewPosition();
4555 ptCaretViewPos.x = nX;
4556 SetCaretAndGoalViewPosition(ptCaretViewPos);
4557 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4558 EnsureCaretVisible();
4562 void CBaseView::OnNavigatePrevinlinediff()
4564 int nX;
4565 if (GetPrevInlineDiff(nX))
4567 POINT ptCaretViewPos = GetCaretViewPosition();
4568 ptCaretViewPos.x = nX;
4569 SetCaretAndGoalViewPosition(ptCaretViewPos);
4570 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4571 EnsureCaretVisible();
4575 bool CBaseView::HasNextInlineDiff()
4577 int nPos;
4578 return GetNextInlineDiff(nPos);
4581 bool CBaseView::GetNextInlineDiff(int & nPos)
4583 POINT ptCaretViewPos = GetCaretViewPosition();
4584 std::vector<inlineDiffPos> positions;
4585 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4587 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4589 if (it->start > ptCaretViewPos.x)
4591 nPos = (LONG)it->start;
4592 return true;
4594 if (it->end > ptCaretViewPos.x)
4596 nPos = (LONG)it->end;
4597 return true;
4601 return false;
4604 bool CBaseView::HasPrevInlineDiff()
4606 int nPos;
4607 return GetPrevInlineDiff(nPos);
4610 bool CBaseView::GetPrevInlineDiff(int & nPos)
4612 POINT ptCaretViewPos = GetCaretViewPosition();
4613 std::vector<inlineDiffPos> positions;
4614 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4616 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4618 if ( it->end < ptCaretViewPos.x)
4620 nPos = (LONG)it->end;
4621 return true;
4623 if ( it->start < ptCaretViewPos.x)
4625 nPos = (LONG)it->start;
4626 return true;
4630 return false;
4633 CBaseView * CBaseView::GetFirstGoodView()
4635 if (IsViewGood(m_pwndLeft))
4636 return m_pwndLeft;
4637 if (IsViewGood(m_pwndRight))
4638 return m_pwndRight;
4639 if (IsViewGood(m_pwndBottom))
4640 return m_pwndBottom;
4641 return NULL;
4644 void CBaseView::BuildAllScreen2ViewVector()
4646 CBaseView * p_pwndView = GetFirstGoodView();
4647 if (p_pwndView)
4649 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4653 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4655 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4658 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4660 CBaseView * p_pwndView = GetFirstGoodView();
4661 if (p_pwndView)
4663 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4667 void CBaseView::UpdateViewLineNumbers()
4669 int nLineNumber = 0;
4670 int nViewLineCount = GetViewCount();
4671 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4673 int oldLine = (int)GetViewLineNumber(nViewLine);
4674 if (oldLine >= 0)
4675 SetViewLineNumber(nViewLine, nLineNumber++);
4677 m_nDigits = 0;
4680 int CBaseView::CleanEmptyLines()
4682 int nRemovedCount = 0;
4683 int nViewLineCount = GetViewCount();
4684 bool bCheckLeft = IsViewGood(m_pwndLeft);
4685 bool bCheckRight = IsViewGood(m_pwndRight);
4686 bool bCheckBottom = IsViewGood(m_pwndBottom);
4687 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4689 bool bAllEmpty = true;
4690 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4691 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4692 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4693 if (bAllEmpty)
4695 if (bCheckLeft)
4697 m_pwndLeft->RemoveViewData(nViewLine);
4699 if (bCheckRight)
4701 m_pwndRight->RemoveViewData(nViewLine);
4703 if (bCheckBottom)
4705 m_pwndBottom->RemoveViewData(nViewLine);
4707 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4709 SaveUndoStep();
4711 nViewLineCount--;
4712 nRemovedCount++;
4713 continue;
4715 nViewLine++;
4717 return nRemovedCount;
4720 int CBaseView::FindScreenLineForViewLine( int viewLine )
4722 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4725 int CBaseView::CountMultiLines( int nViewLine )
4727 if (m_ScreenedViewLine.empty())
4728 return 0; // in case the view is completely empty
4730 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4732 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4734 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4737 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4739 TScreenedViewLine oScreenedLine;
4740 // tokenize string
4741 int prevpos = 0;
4742 int pos = 0;
4743 while ((pos = multiline.Find('\n', pos)) >= 0)
4745 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4746 pos++;
4747 prevpos = pos;
4749 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4750 oScreenedLine.bSublinesSet = true;
4751 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4753 return CountMultiLines(nViewLine);
4756 /// prepare inline diff cache
4757 LineColors & CBaseView::GetLineColors(int nViewLine)
4759 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4761 if (m_bWhitespaceInlineDiffs)
4763 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4764 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4766 else
4768 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4769 return m_ScreenedViewLine[nViewLine].lineColors;
4772 LineColors oLineColors;
4773 // set main line color
4774 COLORREF crBkgnd, crText;
4775 DiffStates diffState = m_pViewData->GetState(nViewLine);
4776 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4777 oLineColors.SetColor(0, crText, crBkgnd);
4779 do {
4780 if (!m_bShowInlineDiff)
4781 break;
4783 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4784 break;
4786 CString sLine = GetViewLineChars(nViewLine);
4787 if (sLine.IsEmpty())
4788 break;
4789 if (!m_pOtherView)
4790 break;
4792 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4793 if (sDiffLine.IsEmpty())
4794 break;
4796 svn_diff_t * diff = NULL;
4797 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4798 break;
4799 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4800 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4801 break;
4803 int lineoffset = 0;
4804 int nTextStartOffset = 0;
4805 std::map<int, COLORREF> removedPositions;
4806 while (diff)
4808 apr_off_t len = diff->original_length;
4810 CString s;
4811 for (int i = 0; i < len; ++i)
4813 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4814 lineoffset++;
4816 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4817 int nTextLength = s.GetLength();
4819 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4820 if ((m_bShowInlineDiff)&&(bInlineDiff))
4822 crBkgnd = InlineViewLineDiffColor(nViewLine);
4824 else
4826 crBkgnd = m_ModifiedBk;
4829 if (len < diff->modified_length)
4831 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4833 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4835 nTextStartOffset += nTextLength;
4836 diff = diff->next;
4838 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4840 oLineColors.AddShotColor(it->first, it->second);
4842 } while (false); // error catch
4844 if (!m_bWhitespaceInlineDiffs)
4846 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4847 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4849 else
4851 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4852 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4855 return GetLineColors(nViewLine);
4858 void CBaseView::OnEditSelectall()
4860 if (m_pViewData == nullptr)
4861 return;
4862 int nLastViewLine = m_pViewData->GetCount()-1;
4863 if (nLastViewLine < 0)
4864 return;
4865 SetupAllViewSelection(0, nLastViewLine);
4867 CString sLine = GetViewLineChars(nLastViewLine);
4868 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4869 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4870 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4872 UpdateWindow();
4875 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4877 FilterWhitespaces(first);
4878 FilterWhitespaces(second);
4881 void CBaseView::FilterWhitespaces(CString& line)
4883 line.Remove(' ');
4884 line.Remove('\t');
4885 line.Remove('\r');
4886 line.Remove('\n');
4889 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4891 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4892 int nEventLine = nLineFromTop + m_nTopLine;
4893 nEventLine--; //we need the index
4894 return nEventLine;
4898 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4900 if (RelayTrippleClick(pMsg))
4901 return TRUE;
4902 return CView::PreTranslateMessage(pMsg);
4906 void CBaseView::ResetUndoStep()
4908 m_AllState.Clear();
4911 void CBaseView::SaveUndoStep()
4913 if (!m_AllState.IsEmpty())
4915 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4917 ResetUndoStep();
4920 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4922 m_pState->addedlines.push_back(index);
4923 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4926 void CBaseView::InsertViewData( int index, const viewdata& data )
4928 m_pState->addedlines.push_back(index);
4929 m_pViewData->InsertData(index, data);
4932 void CBaseView::RemoveViewData( int index )
4934 m_pState->removedlines[index] = m_pViewData->GetData(index);
4935 m_pViewData->RemoveData(index);
4938 void CBaseView::SetViewData( int index, const viewdata& data )
4940 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4941 m_pViewData->SetData(index, data);
4944 void CBaseView::SetViewState( int index, DiffStates state )
4946 m_pState->linestates[index] = m_pViewData->GetState(index);
4947 m_pViewData->SetState(index, state);
4950 void CBaseView::SetViewLine( int index, const CString& sLine )
4952 m_pState->difflines[index] = m_pViewData->GetLine(index);
4953 m_pViewData->SetLine(index, sLine);
4956 void CBaseView::SetViewLineNumber( int index, int linenumber )
4958 int oldLineNumber = m_pViewData->GetLineNumber(index);
4959 if (oldLineNumber != linenumber) {
4960 m_pState->linelines[index] = oldLineNumber;
4961 m_pViewData->SetLineNumber(index, linenumber);
4965 void CBaseView::SetViewLineEnding( int index, EOL ending )
4967 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4968 m_pViewData->SetLineEnding(index, ending);
4971 void CBaseView::SetViewMarked( int index, bool marked )
4973 m_pViewData->SetMarked(index, marked);
4977 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4979 if (HasSelection())
4981 start = m_nSelViewBlockStart;
4982 end = m_nSelViewBlockEnd;
4983 return true;
4985 return false;
4988 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4990 RebuildIfNecessary();
4991 if (size() <= screenLine)
4992 return 0;
4993 return m_Screen2View[screenLine].nViewLine;
4996 int CBaseView::Screen2View::size()
4998 RebuildIfNecessary();
4999 return (int)m_Screen2View.size();
5002 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
5004 RebuildIfNecessary();
5005 if (size() <= screenLine)
5006 return 0;
5007 return m_Screen2View[screenLine].nViewSubLine;
5010 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
5012 RebuildIfNecessary();
5013 return m_Screen2View[screenLine];
5017 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5019 void CBaseView::Screen2View::RebuildIfNecessary()
5021 if (!m_pViewData)
5022 return; // rebuild not necessary
5024 FixScreenedCacheSize(m_pwndLeft);
5025 FixScreenedCacheSize(m_pwndRight);
5026 FixScreenedCacheSize(m_pwndBottom);
5027 if (!m_bFull)
5029 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5031 ResetScreenedViewLineCache(m_pwndLeft, *it);
5032 ResetScreenedViewLineCache(m_pwndRight, *it);
5033 ResetScreenedViewLineCache(m_pwndBottom, *it);
5036 else
5038 ResetScreenedViewLineCache(m_pwndLeft);
5039 ResetScreenedViewLineCache(m_pwndRight);
5040 ResetScreenedViewLineCache(m_pwndBottom);
5042 m_RebuildRanges.clear();
5043 m_bFull = false;
5045 size_t OldSize = m_Screen2View.size();
5046 m_Screen2View.clear();
5047 m_Screen2View.reserve(OldSize); // guess same size
5048 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5050 if (m_pMainFrame->m_bCollapsed)
5052 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5053 ++i;
5054 if (!(i < m_pViewData->GetCount()))
5055 break;
5057 TScreenLineInfo oLineInfo;
5058 oLineInfo.nViewLine = i;
5059 oLineInfo.nViewSubLine = -1; // no wrap
5060 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5062 int nMaxLines = 0;
5063 if (IsLeftViewGood())
5064 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5065 if (IsRightViewGood())
5066 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5067 if (IsBottomViewGood())
5068 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5069 for (int l = 0; l < (nMaxLines-1); ++l)
5071 oLineInfo.nViewSubLine++;
5072 m_Screen2View.push_back(oLineInfo);
5074 oLineInfo.nViewSubLine++;
5076 m_Screen2View.push_back(oLineInfo);
5078 m_pViewData = NULL;
5080 if (IsLeftViewGood())
5081 m_pwndLeft->BuildMarkedWordArray();
5082 if (IsRightViewGood())
5083 m_pwndRight->BuildMarkedWordArray();
5084 if (IsBottomViewGood())
5085 m_pwndBottom->BuildMarkedWordArray();
5086 UpdateLocator();
5087 RecalcAllVertScrollBars();
5088 RecalcAllHorzScrollBars();
5091 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5093 RebuildIfNecessary();
5095 int nScreenLineCount = (int)m_Screen2View.size();
5097 int nPos = 0;
5098 if (nScreenLineCount>16)
5100 // for enough long data search for last screen
5101 // with viewline less than one we are looking for
5102 // use approximate method (based on) binary search using asymmetric start point
5103 // in form 2**n (determined as MSB of length) to go around division and rounding;
5104 // this effectively looks for bit values from MSB to LSB
5106 int nTestBit;
5107 //GetMostSignificantBitValue
5108 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5109 nTestBit = nScreenLineCount;
5110 nTestBit |= nTestBit>>1;
5111 nTestBit |= nTestBit>>2;
5112 nTestBit |= nTestBit>>4;
5113 nTestBit |= nTestBit>>8;
5114 nTestBit |= nTestBit>>16;
5115 nTestBit ^= (nTestBit>>1);
5117 while (nTestBit)
5119 int nTestPos = nPos | nTestBit;
5120 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5122 nPos = nTestPos;
5124 nTestBit >>= 1;
5127 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5129 nPos++;
5132 return nPos;
5135 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5136 m_bFull = true;
5138 m_pViewData = pViewData;
5141 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5143 if (m_bFull)
5144 return;
5146 m_pViewData = pViewData;
5148 TRebuildRange Range;
5149 Range.FirstViewLine=nFirstViewLine;
5150 Range.LastViewLine=nLastViewLine;
5151 m_RebuildRanges.push_back(Range);
5154 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5156 if (!IsViewGood(pwndView))
5158 return false;
5160 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5161 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5162 if (nOldSize == nViewCount)
5164 return false;
5166 pwndView->m_ScreenedViewLine.resize(nViewCount);
5167 return true;
5170 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5172 if (!IsViewGood(pwndView))
5174 return false;
5176 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5177 ResetScreenedViewLineCache(pwndView, Range);
5178 return true;
5181 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5183 if (!IsViewGood(pwndView))
5185 return false;
5187 if (Range.LastViewLine == -1)
5189 return false;
5191 ASSERT(Range.FirstViewLine >= 0);
5192 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5193 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5195 pwndView->m_ScreenedViewLine[i].Clear();
5197 return false;
5200 void CBaseView::WrapChanged()
5202 m_nMaxLineLength = -1;
5203 m_nOffsetChar = 0;
5206 void CBaseView::OnEditFind()
5208 if (m_pFindDialog)
5209 return;
5211 m_pFindDialog = new CFindDlg(this);
5212 m_pFindDialog->Create(this);
5214 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5215 m_pFindDialog->SetReadonly(m_bReadonly);
5218 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5220 ASSERT(m_pFindDialog != NULL);
5222 if (m_pFindDialog->IsTerminating())
5224 // invalidate the handle identifying the dialog box.
5225 m_pFindDialog = NULL;
5226 return 0;
5229 if(m_pFindDialog->FindNext())
5231 //read data from dialog
5232 m_sFindText = m_pFindDialog->GetFindString();
5233 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5234 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5235 m_bWholeWord = m_pFindDialog->WholeWord();
5237 if (!m_bMatchCase)
5238 m_sFindText = m_sFindText.MakeLower();
5240 BuildFindStringArray();
5241 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5243 if (m_pFindDialog->SearchUp())
5244 OnEditFindprev();
5245 else
5246 OnEditFindnext();
5248 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5250 size_t count = 0;
5251 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5252 count += m_arFindStringLines[i];
5253 CString format;
5254 format.LoadString(IDS_FIND_COUNT);
5255 CString matches;
5256 matches.Format(format, count);
5257 m_pFindDialog->SetStatusText(matches);
5259 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5261 if (!IsWritable())
5262 return 0;
5263 bool bFound = false;
5264 if (m_pFindDialog->SearchUp())
5265 bFound = Search(SearchPrevious, true, true, false);
5266 else
5267 bFound = Search(SearchNext, true, true, false);
5268 if (bFound)
5270 CString sReplaceText = m_pFindDialog->GetReplaceString();
5271 CUndo::GetInstance().BeginGrouping();
5272 RemoveSelectedText();
5273 InsertText(sReplaceText);
5274 CUndo::GetInstance().EndGrouping();
5278 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5280 if (!IsWritable())
5281 return 0;
5282 bool bFound = false;
5283 int replaceCount = 0;
5284 POINT lastPoint = m_ptSelectionViewPosStart;
5285 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5286 CUndo::GetInstance().BeginGrouping();
5289 bFound = Search(SearchNext, true, false, true);
5290 if (bFound)
5292 CString sReplaceText = m_pFindDialog->GetReplaceString();
5293 RemoveSelectedText();
5294 InsertText(sReplaceText);
5295 ++replaceCount;
5297 } while (bFound);
5298 CUndo::GetInstance().EndGrouping();
5299 if (replaceCount == 0)
5300 m_ptSelectionViewPosStart = lastPoint;
5301 CString message;
5302 message.Format(IDS_FIND_REPLACED, replaceCount);
5303 if (m_pFindDialog)
5304 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5309 return 0;
5312 void CBaseView::OnEditFindnextStart()
5314 if (m_pViewData == nullptr)
5315 return;
5316 if (HasTextSelection())
5318 m_sFindText = GetSelectedText();
5319 m_bMatchCase = false;
5320 m_bLimitToDiff = false;
5321 m_bWholeWord = false;
5322 m_sFindText = m_sFindText.MakeLower();
5324 BuildFindStringArray();
5325 OnEditFindnext();
5327 else
5329 m_sFindText.Empty();
5330 BuildFindStringArray();
5334 void CBaseView::OnEditFindprevStart()
5336 if (m_pViewData == nullptr)
5337 return;
5338 if (HasTextSelection())
5340 m_sFindText = GetSelectedText();
5341 m_bMatchCase = false;
5342 m_bLimitToDiff = false;
5343 m_bWholeWord = false;
5344 m_sFindText = m_sFindText.MakeLower();
5346 BuildFindStringArray();
5347 OnEditFindprev();
5349 else
5351 m_sFindText.Empty();
5352 BuildFindStringArray();
5356 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5358 if (srchDir == SearchPrevious)
5360 int laststart = -1;
5361 int laststart2 = -1;
5364 laststart2 = laststart;
5365 laststart = str.Find(m_sFindText, laststart + 1);
5366 } while (laststart >= 0 && laststart < start);
5367 start = laststart2;
5369 else
5370 start = str.Find(m_sFindText, start);
5371 end = start + m_sFindText.GetLength();
5372 bool bStringFound = (start >= 0);
5373 if (bStringFound && m_bWholeWord)
5375 if (start)
5376 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5378 if (bStringFound)
5380 if (str.GetLength() > end)
5381 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5384 return bStringFound;
5387 void CBaseView::OnEditFindprev()
5389 Search(SearchPrevious, false, true, false);
5392 void CBaseView::OnEditFindnext()
5394 Search(SearchNext, false, true, false);
5397 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5399 if (m_sFindText.IsEmpty())
5400 return false;
5401 if(!m_pViewData)
5402 return false;
5404 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5405 POINT end;
5406 end.y = m_pViewData->GetCount()-1;
5407 if (end.y < 0)
5408 return false;
5410 if (srchDir==SearchNext)
5411 end.x = GetViewLineLength(end.y);
5412 else
5414 end.x = m_ptSelectionViewPosStart.x;
5415 start.x = 0;
5418 if (!HasTextSelection())
5420 start.y = m_ptCaretViewPos.y;
5421 if (srchDir==SearchNext)
5422 start.x = m_ptCaretViewPos.x;
5423 else
5425 start.x = 0;
5426 end.x = m_ptCaretViewPos.x;
5429 CString sSelectedText;
5430 int startline = -1;
5431 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5433 if (nViewLine < 0)
5435 if (stopEof)
5436 return false;
5437 nViewLine = m_pViewData->GetCount()-1;
5438 startline = start.y;
5439 if (flashIfNotFound)
5441 if (m_pFindDialog)
5442 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5443 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5446 if (nViewLine > end.y)
5448 if (stopEof)
5449 return false;
5450 nViewLine = 0;
5451 startline = start.y;
5452 if (flashIfNotFound)
5454 if (m_pFindDialog)
5455 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5456 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5459 switch (m_pViewData->GetState(nViewLine))
5461 case DIFFSTATE_EMPTY:
5462 break;
5463 case DIFFSTATE_UNKNOWN:
5464 case DIFFSTATE_NORMAL:
5465 if (m_bLimitToDiff)
5466 break;
5467 case DIFFSTATE_REMOVED:
5468 case DIFFSTATE_REMOVEDWHITESPACE:
5469 case DIFFSTATE_ADDED:
5470 case DIFFSTATE_ADDEDWHITESPACE:
5471 case DIFFSTATE_WHITESPACE:
5472 case DIFFSTATE_WHITESPACE_DIFF:
5473 case DIFFSTATE_CONFLICTED:
5474 case DIFFSTATE_CONFLICTED_IGNORED:
5475 case DIFFSTATE_CONFLICTADDED:
5476 case DIFFSTATE_CONFLICTEMPTY:
5477 case DIFFSTATE_CONFLICTRESOLVED:
5478 case DIFFSTATE_IDENTICALREMOVED:
5479 case DIFFSTATE_IDENTICALADDED:
5480 case DIFFSTATE_THEIRSREMOVED:
5481 case DIFFSTATE_THEIRSADDED:
5482 case DIFFSTATE_YOURSREMOVED:
5483 case DIFFSTATE_YOURSADDED:
5484 case DIFFSTATE_EDITED:
5486 sSelectedText = GetViewLineChars(nViewLine);
5487 if (nViewLine == start.y && startline < 0)
5488 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5489 if (!m_bMatchCase)
5490 sSelectedText = sSelectedText.MakeLower();
5491 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5492 int endfound = 0;
5493 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5495 HighlightViewLines(nViewLine, nViewLine);
5496 m_ptSelectionViewPosStart.x = startfound;
5497 m_ptSelectionViewPosEnd.x = endfound;
5498 if (nViewLine == start.y && startline < 0)
5500 m_ptSelectionViewPosStart.x += start.x;
5501 m_ptSelectionViewPosEnd.x += start.x;
5503 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5504 m_ptSelectionViewPosStart.y = nViewLine;
5505 m_ptSelectionViewPosEnd.y = nViewLine;
5506 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5507 UpdateViewsCaretPosition();
5508 EnsureCaretVisible();
5509 Invalidate();
5510 return true;
5513 break;
5516 if (startline >= 0)
5518 if (nViewLine == startline)
5520 if (flashIfNotFound)
5522 CString message;
5523 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5524 if (m_pFindDialog)
5525 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5526 ::MessageBeep(0xFFFFFFFF);
5527 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5529 break;
5533 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5534 return false;
5537 CString CBaseView::GetSelectedText() const
5539 CString sSelectedText;
5540 POINT start = m_ptSelectionViewPosStart;
5541 POINT end = m_ptSelectionViewPosEnd;
5542 if (!HasTextSelection())
5544 if (!HasSelection())
5545 return sSelectedText;
5546 start.y = m_nSelViewBlockStart;
5547 start.x = 0;
5548 end.y = m_nSelViewBlockEnd;
5549 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5551 if (m_pViewData == nullptr)
5552 return sSelectedText;
5553 // first store the selected lines in one CString
5554 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5556 switch (m_pViewData->GetState(nViewLine))
5558 case DIFFSTATE_EMPTY:
5559 break;
5560 case DIFFSTATE_UNKNOWN:
5561 case DIFFSTATE_NORMAL:
5562 case DIFFSTATE_REMOVED:
5563 case DIFFSTATE_REMOVEDWHITESPACE:
5564 case DIFFSTATE_ADDED:
5565 case DIFFSTATE_ADDEDWHITESPACE:
5566 case DIFFSTATE_WHITESPACE:
5567 case DIFFSTATE_WHITESPACE_DIFF:
5568 case DIFFSTATE_CONFLICTED:
5569 case DIFFSTATE_CONFLICTED_IGNORED:
5570 case DIFFSTATE_CONFLICTADDED:
5571 case DIFFSTATE_CONFLICTEMPTY:
5572 case DIFFSTATE_CONFLICTRESOLVED:
5573 case DIFFSTATE_IDENTICALREMOVED:
5574 case DIFFSTATE_IDENTICALADDED:
5575 case DIFFSTATE_THEIRSREMOVED:
5576 case DIFFSTATE_THEIRSADDED:
5577 case DIFFSTATE_YOURSREMOVED:
5578 case DIFFSTATE_YOURSADDED:
5579 case DIFFSTATE_EDITED:
5580 sSelectedText += GetViewLineChars(nViewLine);
5581 sSelectedText += _T("\r\n");
5582 break;
5585 // remove the non-selected chars from the first line, last line and last \r\n
5586 int nLeftCut = start.x;
5587 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5588 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5589 return sSelectedText;
5592 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5594 hasMods = false;
5595 hasConflicts = false;
5596 hasWhitespaceMods = false;
5598 if (m_pViewData)
5600 for (int i=0; i<m_pViewData->GetCount(); i++)
5602 DiffStates state = m_pViewData->GetState(i);
5603 switch (state)
5605 case DIFFSTATE_ADDED:
5606 case DIFFSTATE_IDENTICALADDED:
5607 case DIFFSTATE_THEIRSADDED:
5608 case DIFFSTATE_YOURSADDED:
5609 case DIFFSTATE_CONFLICTADDED:
5610 case DIFFSTATE_IDENTICALREMOVED:
5611 case DIFFSTATE_REMOVED:
5612 case DIFFSTATE_THEIRSREMOVED:
5613 case DIFFSTATE_YOURSREMOVED:
5614 case DIFFSTATE_EMPTY:
5615 hasMods = true;
5616 break;
5617 case DIFFSTATE_CONFLICTED:
5618 case DIFFSTATE_CONFLICTED_IGNORED:
5619 hasConflicts = true;
5620 break;
5621 case DIFFSTATE_REMOVEDWHITESPACE:
5622 case DIFFSTATE_ADDEDWHITESPACE:
5623 case DIFFSTATE_WHITESPACE:
5624 case DIFFSTATE_WHITESPACE_DIFF:
5625 hasWhitespaceMods = true;
5626 break;
5632 void CBaseView::OnEditGotoline()
5634 if (m_pViewData == NULL)
5635 return;
5636 // find the last and first line number
5637 int nViewLineCount = m_pViewData->GetCount();
5639 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5640 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5642 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5643 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5645 break;
5648 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5650 return;
5652 nLastLineNumber++;
5653 int nFirstLineNumber=1; // first is always 1
5655 CString sText;
5656 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5658 CGotoLineDlg dlg(this);
5659 dlg.SetLabel(sText);
5660 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5661 if (dlg.DoModal() == IDOK)
5663 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5665 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5667 HighlightViewLines(nViewLine, nViewLine);
5668 return;
5674 void CBaseView::OnToggleReadonly()
5676 if (IsReadonlyChangable()) {
5677 SetWritable(IsReadonly());
5681 int CBaseView::SaveFile(int nFlags)
5683 Invalidate();
5684 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5686 CFileTextLines file;
5687 m_SaveParams.m_LineEndings = m_lineendings;
5688 m_SaveParams.m_UnicodeType = m_texttype;
5689 file.SetSaveParams(m_SaveParams);
5691 for (int i=0; i<m_pViewData->GetCount(); i++)
5693 //only copy non-removed lines
5694 DiffStates state = m_pViewData->GetState(i);
5695 switch (state)
5697 case DIFFSTATE_CONFLICTED:
5698 case DIFFSTATE_CONFLICTED_IGNORED:
5700 int first = i;
5701 int last = i;
5704 last++;
5705 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5706 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5707 for (int j=first; j<last; j++)
5709 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5711 file.Add(_T("======="), EOL_NOENDING);
5712 for (int j=first; j<last; j++)
5714 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5716 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5717 i = last-1;
5719 break;
5720 case DIFFSTATE_EMPTY:
5721 break;
5722 case DIFFSTATE_CONFLICTEMPTY:
5723 case DIFFSTATE_IDENTICALREMOVED:
5724 case DIFFSTATE_REMOVED:
5725 case DIFFSTATE_THEIRSREMOVED:
5726 case DIFFSTATE_YOURSREMOVED:
5727 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5728 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5730 // do not save removed lines
5731 break;
5733 default:
5734 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5735 break;
5738 CString filename = m_pWorkingFile->GetFilename();
5739 if (m_pWorkingFile->IsReadonly())
5740 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5741 return -1;
5742 if (!file.Save(filename))
5744 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5745 return -1;
5747 m_pWorkingFile->SetFileName(filename);
5748 m_pWorkingFile->StoreFileAttributes();
5749 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5750 SetModified(FALSE);
5751 CUndo::GetInstance().MarkAsOriginalState(
5752 this == m_pwndLeft,
5753 this == m_pwndRight,
5754 this == m_pwndBottom);
5755 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5756 return 0;
5757 return file.GetCount();
5759 return 1;
5763 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5765 if (m_pWorkingFile)
5767 m_pWorkingFile->SetFileName(sFileName);
5768 return SaveFile(nFlags);
5770 return -1;
5774 EOL CBaseView::GetLineEndings()
5776 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5779 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5781 if (bHasMixedEols)
5783 return EOL_AUTOLINE; // mixed eols - hack value
5785 if (m_lineendings == EOL_AUTOLINE)
5787 return EOL_CRLF;
5789 return m_lineendings;
5792 void CBaseView::ReplaceLineEndings(EOL eEol)
5794 if (eEol == EOL_AUTOLINE)
5796 return;
5798 // set AUTOLINE
5799 m_lineendings = eEol;
5800 // replace all set EOLs
5801 // TODO store line endings and lineendings in undo
5802 //CUndo::BeginGrouping();
5803 for (int i = 0; i < GetViewCount(); ++i)
5805 if (IsLineEmpty(i))
5807 continue;
5809 EOL eLineEol = GetViewLineEnding(i);
5810 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5812 continue;
5814 SetViewLineEnding(i, eEol);
5816 //CUndo::EndGrouping();
5817 //CUndo::saveundostep;
5818 DocumentUpdated();
5819 SetModified();
5822 void CBaseView::SetLineEndingStyle(EOL eEol)
5824 m_lineendings = eEol;
5827 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5829 if (m_texttype == eTextType)
5831 return;
5833 m_texttype = eTextType;
5834 DocumentUpdated();
5835 SetModified();
5838 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5840 if (IsReadonly())
5841 return; // nothing to be changed in read-only view
5842 CEncodingDlg dlg;
5843 dlg.view = CString(MAKEINTRESOURCE(nTextId));
5844 dlg.texttype = m_texttype;
5845 dlg.lineendings = GetLineEndings();
5846 if (dlg.DoModal() != IDOK)
5847 return;
5848 SetTextType(dlg.texttype);
5849 ReplaceLineEndings(dlg.lineendings);
5853 Replaces lines from source view to this
5855 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, bool skipMarked)
5857 if (!IsViewGood(pwndView))
5858 return;
5859 if (!IsWritable())
5860 return;
5861 CUndo::GetInstance().BeginGrouping();
5863 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5865 if (skipMarked && GetViewMarked(viewLine))
5866 continue;
5867 viewdata line = pwndView->GetViewData(viewLine);
5868 if (line.ending != EOL_NOENDING)
5869 line.ending = m_lineendings;
5870 switch (line.state)
5872 case DIFFSTATE_CONFLICTEMPTY:
5873 case DIFFSTATE_UNKNOWN:
5874 line.state = DIFFSTATE_EMPTY;
5875 case DIFFSTATE_EMPTY:
5876 break;
5877 case DIFFSTATE_ADDED:
5878 case DIFFSTATE_CONFLICTADDED:
5879 case DIFFSTATE_CONFLICTED:
5880 case DIFFSTATE_CONFLICTED_IGNORED:
5881 case DIFFSTATE_IDENTICALADDED:
5882 case DIFFSTATE_THEIRSADDED:
5883 case DIFFSTATE_YOURSADDED:
5884 case DIFFSTATE_IDENTICALREMOVED:
5885 case DIFFSTATE_REMOVED:
5886 case DIFFSTATE_THEIRSREMOVED:
5887 case DIFFSTATE_YOURSREMOVED:
5888 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5889 line.state = DIFFSTATE_NORMAL;
5890 case DIFFSTATE_NORMAL:
5891 break;
5892 default:
5893 break;
5895 SetViewData(viewLine, line);
5896 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5898 // if this view is in ASCII and the other is not, we have to make sure that
5899 // the text we copy from the other view can actually be saved in ASCII encoding.
5900 // if not, we have to change this views encoding to the same encoding as the other view
5901 BOOL useDefault = FALSE;
5902 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5903 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5904 SetTextType(pwndView->GetTextType());
5907 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5908 // TODO: check if copied line is same as original one set modified only when differ
5909 SetModified();
5910 SaveUndoStep();
5912 int nRemovedLines = CleanEmptyLines();
5913 SaveUndoStep();
5914 //VerifyEols();
5915 // make sure all non empty line have EOL set but last
5916 // wrong can be last copied line(have eol, but no line under),
5917 // or old last line (line before copied block missing eol, but have line under)
5918 // we'll check all lines to be sure
5919 int nLine = GetViewCount();
5920 // check last line have no EOL set
5921 while (--nLine>=0)
5923 if (!IsViewLineEmpty(nLine))
5925 if (GetViewLineEnding(nLine) != EOL_NOENDING)
5927 // we added non last line into empty block on the end (or should we remove eol from this one ?)
5928 // so next line is empty
5929 ASSERT(IsViewLineEmpty(nLine+1));
5930 // and we can turn it to normal empty line
5931 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
5933 break;
5936 // check all (nonlast) line have EOL set
5937 while (--nLine>=0)
5939 if (!IsViewLineEmpty(nLine))
5941 if (GetViewLineEnding(nLine) == EOL_NOENDING)
5943 SetViewLineEnding(nLine, m_lineendings);
5944 // in theory there should be only one line needing fix, but most of time we get over all anyway
5945 // break;
5949 SaveUndoStep();
5950 UpdateViewLineNumbers();
5951 SaveUndoStep();
5953 CUndo::GetInstance().EndGrouping();
5955 if (nRemovedLines!=0)
5957 // some lines are gone update selection
5958 ClearSelection();
5959 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
5961 BuildAllScreen2ViewVector();
5962 pwndView->Invalidate();
5963 RefreshViews();
5966 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
5968 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5969 SetViewMarked(viewLine, marked);
5970 BuildAllScreen2ViewVector();
5971 Invalidate();
5972 RefreshViews();
5975 void CBaseView::UseViewFileExceptMarked(CBaseView *pwndView)
5977 UseViewBlock(pwndView, 0, GetViewCount() - 1, true);
5980 int CBaseView::GetIndentCharsForLine(int x, int y)
5982 const int maxGuessLine = 100;
5983 int nTabMode = -1;
5984 CString line = GetViewLine(y);
5985 if (m_nTabMode & TABMODE_SMARTINDENT)
5987 // detect left char and right char
5988 TCHAR lc = x > 0 ? line[x - 1] : '\0';
5989 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
5990 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
5991 nTabMode = 1;
5992 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
5993 nTabMode = 0;
5994 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
5995 nTabMode = m_nTabMode & TABMODE_USESPACES;
5997 // detect lines nearby
5998 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
6000 bool above = i > 0 && i >= y - maxGuessLine;
6001 bool below = j < GetViewCount() && j <= y + maxGuessLine;
6002 if (!(above || below))
6003 break;
6004 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
6005 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
6006 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
6007 nTabMode = 1;
6008 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
6009 nTabMode = 0;
6010 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
6011 nTabMode = m_nTabMode & TABMODE_USESPACES;
6014 else
6015 nTabMode = m_nTabMode & TABMODE_USESPACES;
6017 if (nTabMode > 0)
6019 // use spaces
6020 x = CountExpandedChars(line, x);
6021 return (m_nTabSize - (x % m_nTabSize));
6024 // use tab
6025 return 0;
6028 void CBaseView::AddIndentationForSelectedBlock()
6030 bool bModified = false;
6031 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6033 // skip the line if no character is selected in the last selected line
6034 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6036 continue;
6038 // skip empty lines
6039 if (IsLineEmpty(nViewLine))
6041 continue;
6043 const CString &sLine = GetViewLine(nViewLine);
6044 CString sTemp = sLine;
6045 if (sTemp.Trim().IsEmpty())
6047 // skip empty and whitechar only lines
6048 continue;
6050 // add tab to line start (alternatively m_nTabSize spaces can be used)
6051 CString tabStr;
6052 int indentChars = GetIndentCharsForLine(0, nViewLine);
6053 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
6054 SetViewLine(nViewLine, tabStr + sLine);
6055 bModified = true;
6057 if (bModified)
6059 SetModified();
6060 SaveUndoStep();
6061 BuildAllScreen2ViewVector();
6065 void CBaseView::RemoveIndentationForSelectedBlock()
6067 bool bModified = false;
6068 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6070 // skip the line if no character is selected in the last selected line
6071 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6073 continue;
6075 // skip empty lines
6076 if (IsLineEmpty(nViewLine))
6078 continue;
6080 CString sLine = GetViewLine(nViewLine);
6081 // remove up to n spaces from line start
6082 // and one tab (if less then n spaces was removed)
6083 int nPos = 0;
6084 while (nPos<m_nTabSize)
6086 switch (sLine[nPos])
6088 case ' ':
6089 nPos++;
6090 continue;
6091 case '\t':
6092 nPos++;
6094 break;
6096 if (nPos>0)
6098 sLine.Delete(0, nPos);
6099 SetViewLine(nViewLine, sLine);
6100 bModified = true;
6103 if (bModified)
6105 SetModified();
6106 SaveUndoStep();
6107 BuildAllScreen2ViewVector();
6112 there are two possible versions
6113 - convert tabs to spaces only in front of text (implemented)
6114 - convert all tabs to spaces
6116 void CBaseView::ConvertTabToSpaces()
6118 bool bModified = false;
6119 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6121 if (IsLineEmpty(nViewLine))
6123 continue;
6125 const CString &sLine = GetViewLine(nViewLine);
6126 bool bTabToConvertFound = false;
6127 int nPosIn = 0;
6128 int nPosOut = 0;
6129 while (nPosIn<sLine.GetLength())
6131 switch (sLine[nPosIn])
6133 case ' ':
6134 nPosIn++;
6135 nPosOut++;
6136 continue;
6137 case '\t':
6138 nPosIn++;
6139 bTabToConvertFound = true;
6140 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6141 continue;
6143 break;
6145 if (bTabToConvertFound)
6147 CString sLineNew = sLine;
6148 sLineNew.Delete(0, nPosIn);
6149 sLineNew = CString(' ', nPosOut) + sLineNew;
6150 SetViewLine(nViewLine, sLineNew);
6151 bModified = true;
6154 if (bModified)
6156 SetModified();
6157 SaveUndoStep();
6158 BuildAllScreen2ViewVector();
6163 there are two possible version
6164 - convert spaces to tabs only in front of text (implemented)
6165 - convert all spaces to tabs
6167 void CBaseView::Tabularize()
6169 bool bModified = false;
6170 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6172 if (IsLineEmpty(nViewLine))
6174 continue;
6176 const CString &sLine = GetViewLine(nViewLine);
6177 int nDel = 0;
6178 int nTabCount = 0; // total tabs to be used
6179 int nSpaceCount = 0; // number of spaces in tab size run
6180 int nPos = 0;
6181 while (nPos<sLine.GetLength())
6183 switch (sLine[nPos++])
6185 case ' ':
6186 //bSpace = true;
6187 if (++nSpaceCount < m_nTabSize)
6189 continue;
6191 case '\t':
6192 nTabCount++;
6193 nSpaceCount = 0;
6194 nDel = nPos;
6195 continue;
6197 break;
6199 if (nDel > 0)
6201 CString sLineNew = sLine;
6202 sLineNew.Delete(0, nDel);
6203 sLineNew = CString('\t', nTabCount) + sLineNew;
6204 if (sLine!=sLineNew)
6206 SetViewLine(nViewLine, sLineNew);
6207 bModified = true;
6211 if (bModified)
6213 SetModified();
6214 SaveUndoStep();
6215 BuildAllScreen2ViewVector();
6219 void CBaseView::RemoveTrailWhiteChars()
6221 bool bModified = false;
6222 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6224 if (IsLineEmpty(nViewLine))
6226 continue;
6228 const CString &sLine = GetViewLine(nViewLine);
6229 CString sLineNew = sLine;
6230 sLineNew.TrimRight();
6231 if (sLine.GetLength()!=sLineNew.GetLength())
6233 SetViewLine(nViewLine, sLineNew);
6234 bModified = true;
6237 if (bModified)
6239 SetModified();
6240 SaveUndoStep();
6241 BuildAllScreen2ViewVector();
6245 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6247 if (GetViewCount()>10000)
6249 // 10k lines is enough to check
6250 TWhitecharsProperties oRet = {true, true, true, true};
6251 return oRet;
6253 TWhitecharsProperties oRet = {};
6254 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6256 if (IsLineEmpty(nViewLine))
6258 continue;
6260 const CString &sLine = GetViewLine(nViewLine);
6261 if (sLine.IsEmpty())
6263 continue;
6265 // check leading whites for convertible tabs and spaces
6266 int nPos = 0;
6267 int nSpaceCount = 0; // number of spaces in tab size run
6268 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6270 switch (sLine[nPos++])
6272 case ' ':
6273 if (++nSpaceCount >= m_nTabSize)
6275 oRet.HasSpacesToConvert = true;
6277 continue;
6278 case '\t':
6279 oRet.HasTabsToConvert = true;
6280 if (nSpaceCount!=0)
6282 oRet.HasSpacesToConvert = true;
6284 continue;
6286 break;
6289 // check trailing whites for removable chars
6290 switch (sLine[sLine.GetLength()-1])
6292 case ' ':
6293 case '\t':
6294 oRet.HasTrailWhiteChars = true;
6297 // check EOLs
6298 EOL eLineEol = GetViewLineEnding(nViewLine);
6299 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6301 oRet.HasMixedEols = true;
6304 return oRet;
6307 void CBaseView::InsertText(const CString& sText)
6309 ResetUndoStep();
6311 POINT ptCaretViewPos = GetCaretViewPosition();
6312 int nLeft = ptCaretViewPos.x;
6313 int nViewLine = ptCaretViewPos.y;
6315 if ((nViewLine == 0) && (GetViewCount() == 0))
6316 OnChar(VK_RETURN, 0, 0);
6318 std::vector<CString> lines;
6319 int nStart = 0;
6320 int nEolPos = 0;
6321 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6323 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6324 lines.push_back(sLine);
6325 nEolPos++;
6326 nStart = nEolPos;
6328 CString sLine = sText.Mid(nStart);
6329 lines.push_back(sLine);
6331 int nLinesToPaste = (int)lines.size();
6332 if (nLinesToPaste > 1)
6334 // multiline text
6336 // We want to undo the multiline insertion in a single step.
6337 CUndo::GetInstance().BeginGrouping();
6339 sLine = GetViewLineChars(nViewLine);
6340 CString sLineLeft = sLine.Left(nLeft);
6341 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6342 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6343 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6344 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6346 newLine.sLine = sLineLeft + lines[0];
6347 SetViewData(nViewLine, newLine);
6350 int nInsertLine = nViewLine;
6351 for (int i = 1; i < nLinesToPaste - 1; i++)
6353 newLine.sLine = lines[i];
6354 InsertViewData(++nInsertLine, newLine);
6356 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6357 newLine.ending = eOriginalEnding;
6358 InsertViewData(++nInsertLine, newLine);
6360 SetModified();
6361 SaveUndoStep();
6363 // adds new lines everywhere except me
6364 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6366 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6368 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6370 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6372 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6374 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6376 SaveUndoStep();
6378 UpdateViewLineNumbers();
6379 CUndo::GetInstance().EndGrouping();
6381 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6383 else
6385 // single line text - just insert it
6386 sLine = GetViewLineChars(nViewLine);
6387 sLine.Insert(nLeft, sText);
6388 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6389 SetViewLine(nViewLine, sLine);
6390 SetViewState(nViewLine, DIFFSTATE_EDITED);
6391 SetModified();
6392 SaveUndoStep();
6395 RefreshViews();
6396 BuildAllScreen2ViewVector();
6397 UpdateCaretViewPosition(ptCaretViewPos);