Unbreak /logmsg and /logmsgfile params of commit command
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob319ace0667d67143d9b087b8adcf333bfc0c80b9
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 void CBaseView::UpdateStatusBar()
267 int nRemovedLines = 0;
268 int nAddedLines = 0;
269 int nConflictedLines = 0;
271 if (m_pViewData)
273 for (int i=0; i<m_pViewData->GetCount(); i++)
275 DiffStates state = m_pViewData->GetState(i);
276 switch (state)
278 case DIFFSTATE_ADDED:
279 case DIFFSTATE_IDENTICALADDED:
280 case DIFFSTATE_THEIRSADDED:
281 case DIFFSTATE_YOURSADDED:
282 case DIFFSTATE_CONFLICTADDED:
283 nAddedLines++;
284 break;
285 case DIFFSTATE_IDENTICALREMOVED:
286 case DIFFSTATE_REMOVED:
287 case DIFFSTATE_THEIRSREMOVED:
288 case DIFFSTATE_YOURSREMOVED:
289 nRemovedLines++;
290 break;
291 case DIFFSTATE_CONFLICTED:
292 case DIFFSTATE_CONFLICTED_IGNORED:
293 nConflictedLines++;
294 break;
299 CString sBarText;
300 CString sTemp;
302 if (m_pwndStatusBar)
304 sBarText += CFileTextLines::GetEncodingName(m_texttype);
305 sBarText += sBarText.IsEmpty() ? L"" : L" ";
306 sBarText += GetEolName(m_lineendings);
307 sBarText += sBarText.IsEmpty() ? L"" : L" ";
309 if (sBarText.IsEmpty())
310 sBarText += _T(" / ");
313 if (nRemovedLines)
315 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
316 if (!sBarText.IsEmpty())
317 sBarText += _T(" / ");
318 sBarText += sTemp;
320 if (nAddedLines)
322 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
323 if (!sBarText.IsEmpty())
324 sBarText += _T(" / ");
325 sBarText += sTemp;
327 if (nConflictedLines)
329 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
330 if (!sBarText.IsEmpty())
331 sBarText += _T(" / ");
332 sBarText += sTemp;
334 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
336 if (m_pwndStatusBar)
338 UINT nID;
339 UINT nStyle;
340 int cxWidth;
341 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
343 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
345 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
347 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
348 sBarText = sTemp+sBarText;
350 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
352 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
353 sBarText = sTemp+sBarText;
355 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
356 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
357 //calculate the width of the text
358 CDC * pDC = m_pwndStatusBar->GetDC();
359 if (pDC)
361 CSize size = pDC->GetTextExtent(sBarText);
362 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
363 ReleaseDC(pDC);
365 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
367 else if (m_pwndRibbonStatusBar)
369 if (!IsViewGood(m_pwndBottom))
370 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
371 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
373 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
374 std::unique_ptr<CMFCRibbonButtonsGroup> apBtnGroupBottom(new CMFCRibbonButtonsGroup);
375 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
376 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
377 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
378 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
379 apBtnGroupBottom->AddButton(pButton);
380 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
381 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
382 apBtnGroupBottom->AddButton(pButton);
383 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
384 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
387 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
388 if (pGroup)
390 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(3));
391 if (pPane)
393 pPane->SetText(sBarText);
395 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
396 if (pButton)
398 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
399 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
401 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
402 if (pButton)
404 pButton->SetText(GetEolName(m_lineendings));
405 pButton->SetDescription(GetEolName(m_lineendings));
408 m_pwndRibbonStatusBar->RecalcLayout();
409 m_pwndRibbonStatusBar->Invalidate();
414 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
416 if (!CView::PreCreateWindow(cs))
417 return FALSE;
419 cs.dwExStyle |= WS_EX_CLIENTEDGE;
420 cs.style &= ~WS_BORDER;
421 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
422 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
424 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
425 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
427 // View must always create its own scrollbars,
428 // if only it's not used within splitter
429 cs.style |= (WS_HSCROLL | WS_VSCROLL);
431 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
432 return TRUE;
435 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
437 int nIndex = 0;
438 if (bBold)
439 nIndex |= 1;
440 if (bItalic)
441 nIndex |= 2;
442 if (m_apFonts[nIndex] == NULL)
444 m_apFonts[nIndex] = new CFont;
445 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
446 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
447 m_lfBaseFont.lfItalic = (BYTE) bItalic;
448 CDC * pDC = GetDC();
449 if (pDC)
451 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
452 ReleaseDC(pDC);
454 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
455 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
457 delete m_apFonts[nIndex];
458 m_apFonts[nIndex] = NULL;
459 return CView::GetFont();
462 return m_apFonts[nIndex];
465 void CBaseView::CalcLineCharDim()
467 CDC *pDC = GetDC();
468 if (pDC == nullptr)
469 return;
470 CFont *pOldFont = pDC->SelectObject(GetFont());
471 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
472 pDC->SelectObject(pOldFont);
473 ReleaseDC(pDC);
475 m_nLineHeight = szCharExt.cy;
476 if (m_nLineHeight <= 0)
477 m_nLineHeight = -1;
478 m_nCharWidth = szCharExt.cx;
479 if (m_nCharWidth <= 0)
480 m_nCharWidth = -1;
483 int CBaseView::GetScreenChars()
485 if (m_nScreenChars == -1)
487 CRect rect;
488 GetClientRect(&rect);
489 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
490 if (m_nScreenChars < 0)
491 m_nScreenChars = 0;
493 return m_nScreenChars;
496 int CBaseView::GetAllMinScreenChars() const
498 int nChars = INT_MAX;
499 if (IsLeftViewGood())
500 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
501 if (IsRightViewGood())
502 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
503 if (IsBottomViewGood())
504 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
505 return (nChars==INT_MAX) ? 0 : nChars;
508 int CBaseView::GetAllMaxLineLength() const
510 int nLength = 0;
511 if (IsLeftViewGood())
512 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
513 if (IsRightViewGood())
514 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
515 if (IsBottomViewGood())
516 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
517 return nLength;
520 int CBaseView::GetLineHeight()
522 if (m_nLineHeight == -1)
523 CalcLineCharDim();
524 if (m_nLineHeight <= 0)
525 return 1;
526 return m_nLineHeight;
529 int CBaseView::GetCharWidth()
531 if (m_nCharWidth == -1)
532 CalcLineCharDim();
533 if (m_nCharWidth <= 0)
534 return 1;
535 return m_nCharWidth;
538 int CBaseView::GetMaxLineLength()
540 if (m_nMaxLineLength == -1)
542 m_nMaxLineLength = 0;
543 int nLineCount = GetLineCount();
544 for (int i=0; i<nLineCount; i++)
546 int nActualLength = GetLineLength(i);
547 if (m_nMaxLineLength < nActualLength)
548 m_nMaxLineLength = nActualLength;
551 return m_nMaxLineLength;
554 int CBaseView::GetLineLength(int index)
556 if (m_pViewData == NULL)
557 return 0;
558 if (m_pViewData->GetCount() == 0)
559 return 0;
560 if ((int)m_Screen2View.size() <= index)
561 return 0;
562 int viewLine = GetViewLineForScreen(index);
563 if (m_pMainFrame->m_bWrapLines)
565 int nLineLength = GetLineChars(index).GetLength();
566 ASSERT(nLineLength >= 0);
567 return nLineLength;
569 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
570 ASSERT(nLineLength >= 0);
571 return nLineLength;
574 int CBaseView::GetViewLineLength(int nViewLine) const
576 if (m_pViewData == NULL)
577 return 0;
578 if (m_pViewData->GetCount() <= nViewLine)
579 return 0;
580 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
581 ASSERT(nLineLength >= 0);
582 return nLineLength;
585 int CBaseView::GetLineCount() const
587 if (m_pViewData == NULL)
588 return 1;
589 int nLineCount = (int)m_Screen2View.size();
590 ASSERT(nLineCount >= 0);
591 return nLineCount;
594 int CBaseView::GetSubLineOffset(int index)
596 return m_Screen2View.GetSubLineOffset(index);
599 CString CBaseView::GetViewLineChars(int nViewLine) const
601 if (m_pViewData == NULL)
602 return 0;
603 if (m_pViewData->GetCount() <= nViewLine)
604 return 0;
605 return m_pViewData->GetLine(nViewLine);
608 CString CBaseView::GetLineChars(int index)
610 if (m_pViewData == NULL)
611 return 0;
612 if (m_pViewData->GetCount() == 0)
613 return 0;
614 if ((int)m_Screen2View.size() <= index)
615 return 0;
616 int viewLine = GetViewLineForScreen(index);
617 if (m_pMainFrame->m_bWrapLines)
619 int subLine = GetSubLineOffset(index);
620 if (subLine >= 0)
622 if (subLine < CountMultiLines(viewLine))
624 return m_ScreenedViewLine[viewLine].SubLines[subLine];
626 return L"";
629 return m_pViewData->GetLine(viewLine);
632 void CBaseView::CheckOtherView()
634 if (m_bOtherDiffChecked)
635 return;
636 // find out what the 'other' file is
637 m_pOtherViewData = NULL;
638 m_pOtherView = NULL;
639 if (this == m_pwndLeft && IsRightViewGood())
641 m_pOtherViewData = m_pwndRight->m_pViewData;
642 m_pOtherView = m_pwndRight;
645 if (this == m_pwndRight && IsLeftViewGood())
647 m_pOtherViewData = m_pwndLeft->m_pViewData;
648 m_pOtherView = m_pwndLeft;
651 m_bOtherDiffChecked = true;
655 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
657 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
658 ASSERT(viewData);
660 DiffStates origstate = viewData->GetState(nLineIndex);
662 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
663 nStartBlock = nLineIndex;
664 nEndBlock = nLineIndex;
665 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
667 DiffStates state = viewData->GetState(nStartBlock - 1);
668 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
669 origstate = state;
670 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
671 nStartBlock--;
672 else
673 break;
675 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
677 DiffStates state = viewData->GetState(nEndBlock + 1);
678 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
679 origstate = state;
680 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
681 nEndBlock++;
682 else
683 break;
687 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
689 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
691 int len = 0;
692 for (int i = nStartBlock; i <= nEndBlock; ++i)
693 len += viewData->GetLine(i).GetLength()+2;
695 CString block;
696 // do not check for whitespace blocks if the line is too long, because
697 // reserving a lot of memory here takes too much time (performance hog)
698 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
699 return block;
700 block.Preallocate(len+1);
701 for (int i = nStartBlock; i <= nEndBlock; ++i)
703 block += viewData->GetLine(i);
704 block += m_Eols[viewData->GetLineEnding(i)];
706 return block;
709 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
711 if (m_pViewData == NULL)
712 return false;
713 bIdentical = false;
714 CheckOtherView();
715 if (!m_pOtherViewData)
716 return false;
717 int viewLine = GetViewLineForScreen(nLineIndex);
718 if (
719 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
720 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
723 bIdentical = true;
724 return false;
726 // first check whether the line itself only has whitespace changes
727 CString mine = m_pViewData->GetLine(viewLine);
728 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
729 if (mine.IsEmpty() && other.IsEmpty())
731 bIdentical = true;
732 return false;
735 if (mine == other)
737 bIdentical = true;
738 return true;
740 FilterWhitespaces(mine, other);
741 if (mine == other)
742 return true;
744 int nStartBlock2, nEndBlock2;
745 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
746 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
747 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
748 if (mine.IsEmpty())
749 bIdentical = false;
750 else
752 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
753 bIdentical = mine == other;
754 FilterWhitespaces(mine, other);
757 return (!mine.IsEmpty()) && (mine == other);
760 bool CBaseView::IsViewLineHidden(int nViewLine)
762 return IsViewLineHidden(m_pViewData, nViewLine);
765 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
767 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
770 int CBaseView::GetLineNumber(int index) const
772 if (m_pViewData == NULL)
773 return -1;
774 int viewLine = GetViewLineForScreen(index);
775 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
776 return -1;
777 return m_pViewData->GetLineNumber(viewLine);
780 int CBaseView::GetScreenLines()
782 if (m_nScreenLines == -1)
784 SCROLLBARINFO sbi;
785 sbi.cbSize = sizeof(sbi);
786 int scrollBarHeight = 0;
787 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
788 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
789 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
790 scrollBarHeight = 0;
791 CRect rect;
792 GetClientRect(&rect);
793 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
794 if (m_nScreenLines < 0)
795 m_nScreenLines = 0;
797 return m_nScreenLines;
800 int CBaseView::GetAllMinScreenLines() const
802 int nLines = INT_MAX;
803 if (IsLeftViewGood())
804 nLines = m_pwndLeft->GetScreenLines();
805 if (IsRightViewGood())
806 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
807 if (IsBottomViewGood())
808 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
809 return (nLines==INT_MAX) ? 0 : nLines;
812 int CBaseView::GetAllLineCount() const
814 int nLines = 0;
815 if (IsLeftViewGood())
816 nLines = m_pwndLeft->GetLineCount();
817 if (IsRightViewGood())
818 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
819 if (IsBottomViewGood())
820 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
821 return nLines;
824 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
826 if (IsLeftViewGood())
827 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
828 if (IsRightViewGood())
829 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
830 if (IsBottomViewGood())
831 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
834 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
836 SCROLLINFO si;
837 si.cbSize = sizeof(si);
838 if (bPositionOnly)
840 si.fMask = SIF_POS;
841 si.nPos = m_nTopLine;
843 else
845 EnableScrollBarCtrl(SB_VERT, TRUE);
846 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
848 m_nTopLine = 0;
849 Invalidate();
851 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
852 si.nMin = 0;
853 si.nMax = GetAllLineCount();
854 si.nPage = GetAllMinScreenLines();
855 si.nPos = m_nTopLine;
857 VERIFY(SetScrollInfo(SB_VERT, &si));
860 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
862 CView::OnVScroll(nSBCode, nPos, pScrollBar);
863 if (m_pwndLeft)
864 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
865 if (m_pwndRight)
866 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
867 if (m_pwndBottom)
868 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
869 if (m_pwndLocator)
870 m_pwndLocator->Invalidate();
873 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
875 // Note we cannot use nPos because of its 16-bit nature
876 SCROLLINFO si;
877 si.cbSize = sizeof(si);
878 si.fMask = SIF_ALL;
879 VERIFY(master->GetScrollInfo(SB_VERT, &si));
881 int nPageLines = GetScreenLines();
882 int nLineCount = GetLineCount();
884 int nNewTopLine;
886 static LONG textwidth = 0;
887 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
888 switch (nSBCode)
890 case SB_TOP:
891 nNewTopLine = 0;
892 break;
893 case SB_BOTTOM:
894 nNewTopLine = nLineCount - nPageLines + 1;
895 break;
896 case SB_LINEUP:
897 nNewTopLine = m_nTopLine - 1;
898 break;
899 case SB_LINEDOWN:
900 nNewTopLine = m_nTopLine + 1;
901 break;
902 case SB_PAGEUP:
903 nNewTopLine = m_nTopLine - si.nPage + 1;
904 break;
905 case SB_PAGEDOWN:
906 nNewTopLine = m_nTopLine + si.nPage - 1;
907 break;
908 case SB_THUMBPOSITION:
909 m_ScrollTool.Clear();
910 nNewTopLine = si.nTrackPos;
911 textwidth = 0;
912 break;
913 case SB_THUMBTRACK:
914 nNewTopLine = si.nTrackPos;
915 if (GetFocus() == this)
917 RECT thumbrect;
918 GetClientRect(&thumbrect);
919 ClientToScreen(&thumbrect);
921 POINT thumbpoint;
922 thumbpoint.x = thumbrect.right;
923 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
924 m_ScrollTool.Init(&thumbpoint);
925 if (textwidth == 0)
927 CString sTemp = sFormat;
928 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
929 textwidth = m_ScrollTool.GetTextWidth(sTemp);
931 thumbpoint.x -= textwidth;
932 int line = GetLineNumber(nNewTopLine);
933 if (line >= 0)
934 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
935 else
936 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
938 break;
939 default:
940 return;
943 if (nNewTopLine < 0)
944 nNewTopLine = 0;
945 if (nNewTopLine >= nLineCount)
946 nNewTopLine = nLineCount - 1;
947 ScrollToLine(nNewTopLine);
950 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
952 if (IsLeftViewGood())
953 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
954 if (IsRightViewGood())
955 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
956 if (IsBottomViewGood())
957 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
960 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
962 SCROLLINFO si;
963 si.cbSize = sizeof(si);
964 if (bPositionOnly)
966 si.fMask = SIF_POS;
967 si.nPos = m_nOffsetChar;
969 else
971 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
972 if (!m_pMainFrame->m_bWrapLines)
974 int minScreenChars = GetAllMinScreenChars();
975 int maxLineLength = GetAllMaxLineLength();
976 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
978 m_nOffsetChar = 0;
979 Invalidate();
981 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
982 si.nMin = 0;
983 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
984 si.nMax += GetMarginWidth()/GetCharWidth();
985 si.nPage = GetScreenChars();
986 si.nPos = m_nOffsetChar;
989 VERIFY(SetScrollInfo(SB_HORZ, &si));
992 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
994 CView::OnHScroll(nSBCode, nPos, pScrollBar);
995 if (m_pwndLeft)
996 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
997 if (m_pwndRight)
998 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
999 if (m_pwndBottom)
1000 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1001 if (m_pwndLocator)
1002 m_pwndLocator->Invalidate();
1005 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1007 SCROLLINFO si;
1008 si.cbSize = sizeof(si);
1009 si.fMask = SIF_ALL;
1010 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1012 int nPageChars = GetScreenChars();
1013 int nMaxLineLength = GetMaxLineLength();
1015 int nNewOffset;
1016 switch (nSBCode)
1018 case SB_LEFT:
1019 nNewOffset = 0;
1020 break;
1021 case SB_BOTTOM:
1022 nNewOffset = nMaxLineLength - nPageChars + 1;
1023 break;
1024 case SB_LINEUP:
1025 nNewOffset = m_nOffsetChar - 1;
1026 break;
1027 case SB_LINEDOWN:
1028 nNewOffset = m_nOffsetChar + 1;
1029 break;
1030 case SB_PAGEUP:
1031 nNewOffset = m_nOffsetChar - si.nPage + 1;
1032 break;
1033 case SB_PAGEDOWN:
1034 nNewOffset = m_nOffsetChar + si.nPage - 1;
1035 break;
1036 case SB_THUMBPOSITION:
1037 case SB_THUMBTRACK:
1038 nNewOffset = si.nTrackPos;
1039 break;
1040 default:
1041 return;
1044 if (nNewOffset >= nMaxLineLength)
1045 nNewOffset = nMaxLineLength - 1;
1046 if (nNewOffset < 0)
1047 nNewOffset = 0;
1048 ScrollToChar(nNewOffset, TRUE);
1051 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1053 if (m_nOffsetChar != nNewOffsetChar)
1055 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1056 m_nOffsetChar = nNewOffsetChar;
1057 CRect rcScroll;
1058 GetClientRect(&rcScroll);
1059 rcScroll.left += GetMarginWidth();
1060 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1061 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1062 // update the view header
1063 rcScroll.left = 0;
1064 rcScroll.top = 0;
1065 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1066 InvalidateRect(&rcScroll, FALSE);
1067 UpdateWindow();
1068 if (bTrackScrollBar)
1069 RecalcHorzScrollBar(TRUE);
1070 UpdateCaret();
1071 if (m_pwndLineDiffBar)
1072 m_pwndLineDiffBar->Invalidate();
1076 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1078 if (m_pwndLeft)
1079 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1080 if (m_pwndRight)
1081 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1082 if (m_pwndBottom)
1083 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1086 void CBaseView::ScrollAllSide(int delta)
1088 int nNewOffset = m_nOffsetChar;
1089 nNewOffset += delta;
1090 int nMaxLineLength = GetMaxLineLength();
1091 if (nNewOffset >= nMaxLineLength)
1092 nNewOffset = nMaxLineLength - 1;
1093 if (nNewOffset < 0)
1094 nNewOffset = 0;
1095 ScrollAllToChar(nNewOffset, TRUE);
1096 if (m_pwndLineDiffBar)
1097 m_pwndLineDiffBar->Invalidate();
1098 UpdateCaret();
1101 void CBaseView::ScrollSide(int delta)
1103 int nNewOffset = m_nOffsetChar;
1104 nNewOffset += delta;
1105 int nMaxLineLength = GetMaxLineLength();
1106 if (nNewOffset >= nMaxLineLength)
1107 nNewOffset = nMaxLineLength - 1;
1108 if (nNewOffset < 0)
1109 nNewOffset = 0;
1110 ScrollToChar(nNewOffset, TRUE);
1111 if (m_pwndLineDiffBar)
1112 m_pwndLineDiffBar->Invalidate();
1113 UpdateCaret();
1116 void CBaseView::ScrollVertical(short zDelta)
1118 const int nLineCount = GetLineCount();
1119 int nTopLine = m_nTopLine;
1120 nTopLine -= (zDelta/30);
1121 if (nTopLine < 0)
1122 nTopLine = 0;
1123 if (nTopLine >= nLineCount)
1124 nTopLine = nLineCount - 1;
1125 ScrollToLine(nTopLine, TRUE);
1128 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1130 if (m_nTopLine != nNewTopLine)
1132 if (nNewTopLine < 0)
1133 nNewTopLine = 0;
1135 int nScrollLines = m_nTopLine - nNewTopLine;
1137 m_nTopLine = nNewTopLine;
1138 CRect rcScroll;
1139 GetClientRect(&rcScroll);
1140 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1141 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1142 UpdateWindow();
1143 if (bTrackScrollBar)
1144 RecalcVertScrollBar(TRUE);
1145 UpdateCaret();
1150 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1152 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1154 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1156 int nViewLine = GetViewLineForScreen(nLineIndex);
1157 HICON icon = NULL;
1158 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1159 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1160 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1162 DiffStates state = m_pViewData->GetState(nViewLine);
1163 switch (state)
1165 case DIFFSTATE_ADDED:
1166 case DIFFSTATE_THEIRSADDED:
1167 case DIFFSTATE_YOURSADDED:
1168 case DIFFSTATE_IDENTICALADDED:
1169 case DIFFSTATE_CONFLICTADDED:
1170 eIcon = TScreenedViewLine::ICN_ADD;
1171 break;
1172 case DIFFSTATE_REMOVED:
1173 case DIFFSTATE_THEIRSREMOVED:
1174 case DIFFSTATE_YOURSREMOVED:
1175 case DIFFSTATE_IDENTICALREMOVED:
1176 eIcon = TScreenedViewLine::ICN_REMOVED;
1177 break;
1178 case DIFFSTATE_CONFLICTED:
1179 eIcon = TScreenedViewLine::ICN_CONFLICT;
1180 break;
1181 case DIFFSTATE_CONFLICTED_IGNORED:
1182 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1183 break;
1184 case DIFFSTATE_EDITED:
1185 eIcon = TScreenedViewLine::ICN_EDIT;
1186 break;
1187 default:
1188 break;
1190 bool bIdentical = false;
1191 int blockstart = -1;
1192 int blockend = -1;
1193 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1195 if (bIdentical)
1196 eIcon = TScreenedViewLine::ICN_SAME;
1197 else
1198 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1199 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1201 if (nViewLine > blockstart)
1202 Invalidate(); // redraw the upper icons since they're now changing
1203 while (blockstart <= blockend)
1204 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1207 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1208 eIcon = TScreenedViewLine::ICN_MOVED;
1209 if (m_pViewData->GetMarked(nViewLine))
1210 eIcon = TScreenedViewLine::ICN_MARKED;
1211 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1213 switch (eIcon)
1215 case TScreenedViewLine::ICN_UNKNOWN:
1216 case TScreenedViewLine::ICN_NONE:
1217 break;
1218 case TScreenedViewLine::ICN_SAME:
1219 icon = m_hEqualIcon;
1220 break;
1221 case TScreenedViewLine::ICN_EDIT:
1222 icon = m_hEditedIcon;
1223 break;
1224 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1225 icon = m_hWhitespaceBlockIcon;
1226 break;
1227 case TScreenedViewLine::ICN_ADD:
1228 icon = m_hAddedIcon;
1229 break;
1230 case TScreenedViewLine::ICN_CONFLICT:
1231 icon = m_hConflictedIcon;
1232 break;
1233 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1234 icon = m_hConflictedIgnoredIcon;
1235 break;
1236 case TScreenedViewLine::ICN_REMOVED:
1237 icon = m_hRemovedIcon;
1238 break;
1239 case TScreenedViewLine::ICN_MOVED:
1240 icon = m_hMovedIcon;
1241 break;
1242 case TScreenedViewLine::ICN_MARKED:
1243 icon = m_hMarkedIcon;
1244 break;
1248 if (icon)
1250 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1252 if ((m_bViewLinenumbers)&&(m_nDigits))
1254 int nSubLine = GetSubLineOffset(nLineIndex);
1255 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1256 CString sLinenumber;
1257 if (bIsFirstSubline)
1259 CString sLinenumberFormat;
1260 int nLineNumber = GetLineNumber(nLineIndex);
1261 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1263 // TODO: do not show if there is no number hidden
1264 // TODO: show number if there is only one
1265 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1266 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1268 else if (nLineNumber >= 0)
1270 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1271 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1273 else if (m_pMainFrame->m_bWrapLines)
1275 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1276 sLinenumber.Format(sLinenumberFormat, _T("·"));
1278 if (!sLinenumber.IsEmpty())
1280 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1281 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1283 pdc->SelectObject(GetFont());
1284 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1291 int CBaseView::GetMarginWidth()
1293 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1295 if (m_nDigits <= 0)
1297 int nLength = (int)m_pViewData->GetCount();
1298 // find out how many digits are needed to show the highest line number
1299 CString sMax;
1300 sMax.Format(_T("%d"), nLength);
1301 m_nDigits = sMax.GetLength();
1303 int nWidth = GetCharWidth();
1304 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1306 return MARGINWIDTH;
1309 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1311 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1312 COLORREF crBk, crFg;
1313 if (IsBottomViewGood())
1315 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1316 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1318 else
1320 DiffStates state = DIFFSTATE_REMOVED;
1321 if (this == m_pwndRight)
1323 state = DIFFSTATE_ADDED;
1325 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1327 pdc->SetBkColor(crBk);
1328 pdc->FillSolidRect(textrect, crBk);
1330 pdc->SetTextColor(crFg);
1332 pdc->SelectObject(GetFont(FALSE, TRUE));
1334 CString sViewTitle;
1335 if (IsModified())
1337 sViewTitle = _T("* ") + m_sWindowName;
1339 else
1341 sViewTitle = m_sWindowName;
1343 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1344 if (nStringLength > rect.Width())
1346 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1347 sViewTitle = m_sWindowName.Mid(offset);
1349 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1350 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1351 if (this->GetFocus() == this)
1352 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1353 else
1354 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1357 void CBaseView::OnDraw(CDC * pDC)
1359 CRect rcClient;
1360 GetClientRect(rcClient);
1362 int nLineCount = GetLineCount();
1363 int nLineHeight = GetLineHeight();
1365 CDC cacheDC;
1366 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1367 if (m_pCacheBitmap == NULL)
1369 m_pCacheBitmap = new CBitmap;
1370 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1372 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1374 DrawHeader(pDC, rcClient);
1376 CRect rcLine;
1377 rcLine = rcClient;
1378 rcLine.top += nLineHeight+HEADERHEIGHT;
1379 rcLine.bottom = rcLine.top + nLineHeight;
1380 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1381 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1383 int nCurrentLine = m_nTopLine;
1384 bool bBeyondFileLineCached = false;
1385 while (rcLine.top < rcClient.bottom)
1387 if (nCurrentLine < nLineCount)
1389 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1390 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1391 bBeyondFileLineCached = false;
1393 else if (!bBeyondFileLineCached)
1395 DrawMargin(&cacheDC, rcCacheMargin, -1);
1396 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1397 bBeyondFileLineCached = true;
1400 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1402 nCurrentLine ++;
1403 rcLine.OffsetRect(0, nLineHeight);
1406 cacheDC.SelectObject(pOldBitmap);
1407 cacheDC.DeleteDC();
1410 bool CBaseView::IsStateConflicted(DiffStates state)
1412 switch (state)
1414 case DIFFSTATE_CONFLICTED:
1415 case DIFFSTATE_CONFLICTED_IGNORED:
1416 case DIFFSTATE_CONFLICTEMPTY:
1417 case DIFFSTATE_CONFLICTADDED:
1418 return true;
1420 return false;
1423 bool CBaseView::IsStateEmpty(DiffStates state)
1425 switch (state)
1427 case DIFFSTATE_CONFLICTEMPTY:
1428 case DIFFSTATE_UNKNOWN:
1429 case DIFFSTATE_EMPTY:
1430 return true;
1432 return false;
1435 bool CBaseView::IsStateRemoved(DiffStates state)
1437 switch (state)
1439 case DIFFSTATE_REMOVED:
1440 case DIFFSTATE_THEIRSREMOVED:
1441 case DIFFSTATE_YOURSREMOVED:
1442 case DIFFSTATE_IDENTICALREMOVED:
1443 return true;
1445 return false;
1448 DiffStates CBaseView::ResolveState(DiffStates state)
1450 if (IsStateConflicted(state))
1452 if (state == DIFFSTATE_CONFLICTEMPTY)
1453 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1454 else
1455 return DIFFSTATE_CONFLICTRESOLVED;
1457 return state;
1461 bool CBaseView::IsLineEmpty(int nLineIndex)
1463 if (m_pViewData == 0)
1464 return FALSE;
1465 int nViewLine = GetViewLineForScreen(nLineIndex);
1466 return IsViewLineEmpty(nViewLine);
1469 bool CBaseView::IsViewLineEmpty(int nViewLine)
1471 if (m_pViewData == 0)
1472 return FALSE;
1473 const DiffStates state = m_pViewData->GetState(nViewLine);
1474 return IsStateEmpty(state);
1477 bool CBaseView::IsLineRemoved(int nLineIndex)
1479 if (m_pViewData == 0)
1480 return FALSE;
1481 int nViewLine = GetViewLineForScreen(nLineIndex);
1482 return IsViewLineRemoved(nViewLine);
1485 bool CBaseView::IsViewLineRemoved(int nViewLine)
1487 if (m_pViewData == 0)
1488 return FALSE;
1489 const DiffStates state = m_pViewData->GetState(nViewLine);
1490 return IsStateRemoved(state);
1493 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1495 if (m_pViewData == 0)
1496 return false;
1497 const DiffStates state = m_pViewData->GetState(nLineIndex);
1498 return IsStateConflicted(state);
1501 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1503 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1506 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1508 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1511 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1513 if (origin.x < (rc.left - GetCharWidth() +1))
1514 return;
1515 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1516 return;
1517 int viewLine = GetViewLineForScreen(nLineIndex);
1518 EOL ending = m_pViewData->GetLineEnding(viewLine);
1519 if (m_bIconLFs)
1521 HICON hEndingIcon = NULL;
1522 switch (ending)
1524 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1525 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1526 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1527 default: return;
1529 // If EOL style has changed, color end-of-line markers as inline differences.
1531 m_bShowInlineDiff && m_pOtherViewData &&
1532 (viewLine < m_pOtherViewData->GetCount()) &&
1533 (ending != EOL_NOENDING) &&
1534 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1535 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1538 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1541 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1543 else
1545 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1546 CPen * oldpen = pDC->SelectObject(&pen);
1547 int yMiddle = origin.y + rc.Height()/2;
1548 int xMiddle = origin.x+GetCharWidth()/2;
1549 bool bMultiline = false;
1550 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1552 if (GetLineLength(nLineIndex+1))
1554 // multiline
1555 bMultiline = true;
1556 pDC->MoveTo(origin.x, yMiddle-2);
1557 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1558 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1559 pDC->LineTo(origin.x, yMiddle+2);
1561 else if (GetLineLength(nLineIndex) == 0)
1562 bMultiline = true;
1564 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1565 bMultiline = true;
1567 if (!bMultiline)
1569 switch (ending)
1571 case EOL_AUTOLINE:
1572 case EOL_CRLF:
1573 // arrow from top to middle+2, then left
1574 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1575 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1576 case EOL_CR:
1577 // arrow from right to left
1578 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1579 pDC->LineTo(origin.x, yMiddle);
1580 pDC->LineTo(origin.x+4, yMiddle+4);
1581 pDC->MoveTo(origin.x, yMiddle);
1582 pDC->LineTo(origin.x+4, yMiddle-4);
1583 break;
1584 case EOL_LFCR:
1585 // from right-upper to left then down
1586 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1587 pDC->LineTo(xMiddle, yMiddle-2);
1588 pDC->LineTo(xMiddle, rc.bottom-1);
1589 pDC->LineTo(xMiddle+4, rc.bottom-5);
1590 pDC->MoveTo(xMiddle, rc.bottom-1);
1591 pDC->LineTo(xMiddle-4, rc.bottom-5);
1592 break;
1593 case EOL_LF:
1594 // arrow from top to bottom
1595 pDC->MoveTo(xMiddle, rc.top);
1596 pDC->LineTo(xMiddle, rc.bottom-1);
1597 pDC->LineTo(xMiddle+4, rc.bottom-5);
1598 pDC->MoveTo(xMiddle, rc.bottom-1);
1599 pDC->LineTo(xMiddle-4, rc.bottom-5);
1600 break;
1601 case EOL_FF: // Form Feed, U+000C
1602 case EOL_NEL: // Next Line, U+0085
1603 case EOL_LS: // Line Separator, U+2028
1604 case EOL_PS: // Paragraph Separator, U+2029
1605 // draw a horizontal line at the bottom of this line
1606 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1607 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1608 pDC->LineTo(origin.x, rc.bottom-2);
1609 pDC->LineTo(origin.x+5, rc.bottom-2);
1610 pDC->MoveTo(origin.x, rc.bottom-2);
1611 pDC->LineTo(origin.x+1, rc.bottom-6);
1612 break;
1613 default: // other EOLs
1614 // arrow from top right to bottom left
1615 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1616 pDC->LineTo(origin.x, rc.bottom-1);
1617 pDC->LineTo(origin.x+5, rc.bottom-2);
1618 pDC->MoveTo(origin.x, rc.bottom-1);
1619 pDC->LineTo(origin.x+1, rc.bottom-6);
1620 break;
1621 case EOL_NOENDING:
1622 break;
1625 pDC->SelectObject(oldpen);
1629 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1631 if (!m_bShowSelection)
1632 return;
1634 int nSelBlockStart;
1635 int nSelBlockEnd;
1636 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1637 return;
1639 const int THICKNESS = 2;
1640 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1642 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1643 int nSubLine = GetSubLineOffset(nLineIndex);
1644 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1645 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1647 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1650 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1651 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1653 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1657 void CBaseView::DrawTextLine(
1658 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1660 ASSERT(nLineIndex < GetLineCount());
1661 int nViewLine = GetViewLineForScreen(nLineIndex);
1662 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1664 LineColors lineCols = GetLineColors(nViewLine);
1666 CString sViewLine = GetViewLineChars(nViewLine);
1667 // mark selection
1668 if (m_bShowSelection && HasTextSelection())
1670 // has this line selection ?
1671 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1673 int nViewLineLength = sViewLine.GetLength();
1675 // first suppose the whole line is selected
1676 int selectedStart = 0;
1677 int selectedEnd = nViewLineLength;
1679 // the view line is partially selected
1680 if (m_ptSelectionViewPosStart.y == nViewLine)
1682 selectedStart = m_ptSelectionViewPosStart.x;
1685 if (m_ptSelectionViewPosEnd.y == nViewLine)
1687 selectedEnd = m_ptSelectionViewPosEnd.x;
1689 // apply selection coloring
1690 // First enforce start and end point
1691 lineCols.SplitBlock(selectedStart);
1692 lineCols.SplitBlock(selectedEnd);
1693 // change color of affected parts
1694 long intenseColorScale = m_bFocused ? 70 : 30;
1695 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1696 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1698 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1699 if (it->second.shot == it->second.background)
1701 it->second.shot = crBk;
1703 it->second.background = crBk;
1704 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1709 // TODO: remove duplicate from selection and mark
1710 if (!m_sMarkedWord.IsEmpty())
1712 int nMarkLength = m_sMarkedWord.GetLength();
1713 //int nViewLineLength = sViewLine.GetLength();
1714 const TCHAR * text = sViewLine;
1715 const TCHAR * findText = text;
1716 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1718 int nMarkStart = static_cast<int>(findText - text);
1719 int nMarkEnd = nMarkStart + nMarkLength;
1720 findText += nMarkLength;
1721 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1722 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1723 if (eLeft == eStart)
1724 continue;
1725 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1726 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1727 if (eRight == eEnd)
1728 continue;
1730 // First enforce start and end point
1731 lineCols.SplitBlock(nMarkStart);
1732 lineCols.SplitBlock(nMarkEnd);
1733 // change color of affected parts
1734 const long int nIntenseColorScale = 200;
1735 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1736 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1738 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1739 if (it->second.shot == it->second.background)
1741 it->second.shot = crBk;
1743 it->second.background = crBk;
1744 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1748 if (!m_sFindText.IsEmpty())
1750 int nMarkStart = 0;
1751 int nMarkEnd = 0;
1752 int nStringPos = nMarkStart;
1753 CString searchLine = sViewLine;
1754 if (!m_bMatchCase)
1755 searchLine.MakeLower();
1756 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1758 // First enforce start and end point
1759 lineCols.SplitBlock(nMarkStart+nStringPos);
1760 lineCols.SplitBlock(nMarkEnd+nStringPos);
1761 // change color of affected parts
1762 const long int nIntenseColorScale = 30;
1763 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1764 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1766 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1767 if (it->second.shot == it->second.background)
1769 it->second.shot = crBk;
1771 it->second.background = crBk;
1772 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1774 searchLine = searchLine.Mid(nMarkEnd);
1775 nStringPos = nMarkEnd;
1776 nMarkStart = 0;
1777 nMarkEnd = 0;
1781 // @ this point we may cache data for next line which may be same in wrapped mode
1783 int nTextOffset = 0;
1784 int nSubline = GetSubLineOffset(nLineIndex);
1785 for (int n=0; n<nSubline; n++)
1787 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1788 nTextOffset += sLine.GetLength();
1791 CString sLine = GetLineChars(nLineIndex);
1792 int nLineLength = sLine.GetLength();
1793 CString sLineExp = ExpandChars(sLine);
1794 LPCTSTR textExp = sLineExp;
1795 //int nLineLengthExp = sLineExp.GetLength();
1796 int nStartExp = 0;
1797 int nLeft = coords.x;
1798 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1800 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1801 ++itEnd;
1802 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1803 int nEnd = nLineLength;
1804 if (itEnd != lineCols.end())
1806 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1808 int nBlockLength = nEnd - nStart;
1809 if (nBlockLength > 0 && nEnd>=0)
1811 pDC->SetBkColor(itStart->second.background);
1812 pDC->SetTextColor(itStart->second.text);
1813 int nEndExp = CountExpandedChars(sLine, nEnd);
1814 int nTextLength = nEndExp - nStartExp;
1815 LPCTSTR p_zBlockText = textExp + nStartExp;
1816 SIZE Size;
1817 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1818 int nRight = nLeft + Size.cx;
1819 if ((nRight > rc.left) && (nLeft < rc.right))
1821 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1822 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1823 // is 4094 (4095 doesn't work anymore).
1824 // So we limit the length here to that 4094 chars.
1825 // In case we're scrolled to the right, there's no need to draw the string
1826 // from way outside our window, so we also offset the drawing to the start of the window.
1827 // This reduces the string length as well.
1828 int offset = 0;
1829 int leftcoord = nLeft;
1830 if (nLeft < 0)
1832 int fit = nTextLength;
1833 std::unique_ptr<int> posBuffer(new int[fit]);
1834 GetTextExtentExPoint(pDC->GetSafeHdc(), p_zBlockText, nTextLength, INT_MAX, &fit, posBuffer.get(), &Size);
1835 int lower = 0, upper = fit - 1;
1838 int middle = (upper + lower + 1) / 2;
1839 int width = posBuffer.get()[middle];
1840 if (rc.left - nLeft < width)
1841 upper = middle - 1;
1842 else
1843 lower = middle;
1844 } while (lower < upper);
1846 offset = lower;
1847 nTextLength -= offset;
1848 leftcoord += lower > 0 ? posBuffer.get()[lower - 1] : 0;
1851 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1852 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1854 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1857 nLeft = nRight;
1858 coords.x = nRight;
1859 nStartExp = nEndExp;
1864 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1866 if (nLineIndex >= GetLineCount())
1867 nLineIndex = -1;
1868 ASSERT(nLineIndex >= -1);
1870 if ((nLineIndex == -1) || !m_pViewData)
1872 // Draw line beyond the text
1873 COLORREF crBkgnd, crText;
1874 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1875 pDC->FillSolidRect(rc, crBkgnd);
1876 return;
1879 int viewLine = GetViewLineForScreen(nLineIndex);
1880 if (m_pMainFrame->m_bCollapsed)
1882 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1884 COLORREF crBkgnd, crText;
1885 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1886 pDC->FillSolidRect(rc, crBkgnd);
1888 const int THICKNESS = 2;
1889 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1890 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1891 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1892 pDC->SetBkColor(crBkgnd);
1893 CRect rect = rc;
1894 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1895 return;
1899 DiffStates diffState = m_pViewData->GetState(viewLine);
1900 COLORREF crBkgnd, crText;
1901 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1903 if (diffState == DIFFSTATE_CONFLICTED)
1905 // conflicted lines are shown without 'text' on them
1906 CRect rect = rc;
1907 pDC->FillSolidRect(rc, crBkgnd);
1908 // now draw some faint text patterns
1909 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1910 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1911 DrawBlockLine(pDC, rc, nLineIndex);
1912 return;
1915 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1916 CString sLine = GetLineChars(nLineIndex);
1917 if (sLine.IsEmpty())
1919 pDC->FillSolidRect(rc, crBkgnd);
1920 DrawBlockLine(pDC, rc, nLineIndex);
1921 DrawLineEnding(pDC, rc, nLineIndex, origin);
1922 return;
1925 CheckOtherView();
1927 // Draw the line
1929 pDC->SelectObject(GetFont(FALSE, FALSE));
1931 DrawTextLine(pDC, rc, nLineIndex, origin);
1933 // draw white space after the end of line
1934 CRect frect = rc;
1935 if (origin.x > frect.left)
1936 frect.left = origin.x;
1937 if (frect.right > frect.left)
1938 pDC->FillSolidRect(frect, crBkgnd);
1940 // draw the whitespace chars
1941 LPCTSTR pszChars = (LPCWSTR)sLine;
1942 if (m_bViewWhitespace)
1944 int xpos = 0;
1945 int nChars = 0;
1946 LPCTSTR pLastSpace = pszChars;
1947 int y = rc.top + (rc.bottom-rc.top)/2;
1948 xpos -= m_nOffsetChar * GetCharWidth();
1950 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1951 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1952 while (*pszChars)
1954 switch (*pszChars)
1956 case _T('\t'):
1958 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
1959 pLastSpace = pszChars + 1;
1960 // draw an arrow
1961 int nSpaces = GetTabSize() - nChars % GetTabSize();
1962 if (xpos + nSpaces * GetCharWidth() >= 0)
1964 CPen * oldPen = pDC->SelectObject(&pen);
1965 pDC->MoveTo(xpos + rc.left, y);
1966 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-2, y);
1967 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-6, y-4);
1968 pDC->MoveTo((xpos + nSpaces * GetCharWidth()) + rc.left-2, y);
1969 pDC->LineTo((xpos + nSpaces * GetCharWidth()) + rc.left-6, y+4);
1970 pDC->SelectObject(oldPen);
1972 xpos += nSpaces * GetCharWidth();
1973 nChars += nSpaces;
1975 break;
1976 case _T(' '):
1978 xpos += pDC->GetTextExtent(pLastSpace, (int)(pszChars - pLastSpace)).cx;
1979 pLastSpace = pszChars + 1;
1980 // draw a small dot
1981 CPen * oldPen = pDC->SelectObject(&pen2);
1982 if (xpos + GetCharWidth() >= 0)
1984 pDC->MoveTo(xpos + rc.left + GetCharWidth()/2-1, y);
1985 pDC->LineTo(xpos + rc.left + GetCharWidth()/2+1, y);
1986 pDC->SelectObject(oldPen);
1988 xpos += GetCharWidth();
1989 nChars++;
1991 break;
1992 default:
1993 nChars++;
1994 break;
1996 pszChars++;
1999 DrawBlockLine(pDC, rc, nLineIndex);
2000 if (origin.x >= rc.left)
2001 DrawLineEnding(pDC, rc, nLineIndex, origin);
2004 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
2006 if (nCount <= 0)
2008 line = _T("");
2009 return;
2012 int nTabSize = GetTabSize();
2014 int nActualOffset = CountExpandedChars(sLine, nOffset);
2016 LPCTSTR pszChars = (LPCWSTR)sLine;
2017 pszChars += nOffset;
2018 int nLength = nCount;
2020 int nTabCount = 0;
2021 for (int i=0; i<nLength; i++)
2023 if (pszChars[i] == _T('\t'))
2024 nTabCount ++;
2027 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2028 int nCurPos = 0;
2029 if (nTabCount > 0 || m_bViewWhitespace)
2031 for (int i=0; i<nLength; i++)
2033 if (pszChars[i] == _T('\t'))
2035 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2036 while (nSpaces > 0)
2038 pszBuf[nCurPos ++] = _T(' ');
2039 nSpaces --;
2042 else
2044 pszBuf[nCurPos] = pszChars[i];
2045 nCurPos ++;
2049 else
2051 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2052 nCurPos = nLength;
2054 pszBuf[nCurPos] = 0;
2055 line.ReleaseBuffer();
2058 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2060 CString sRet;
2061 int nLength = sLine.GetLength();
2062 ExpandChars(sLine, nOffset, nLength, sRet);
2063 return sRet;
2066 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2068 int nTabSize = GetTabSize();
2070 int nActualOffset = 0;
2071 for (int i=0; i<nLength; i++)
2073 if (sLine[i] == _T('\t'))
2074 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2075 else
2076 nActualOffset ++;
2078 return nActualOffset;
2081 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2083 if (m_pwndLeft)
2084 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2085 if (m_pwndRight)
2086 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2087 if (m_pwndBottom)
2088 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2089 if (m_pwndLocator)
2090 m_pwndLocator->Invalidate();
2093 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2095 //almost the same as ScrollAllToLine, but try to put the line in the
2096 //middle of the view, not on top
2097 int nNewTopLine = nNewLine - GetScreenLines()/2;
2098 if (nNewTopLine < 0)
2099 nNewTopLine = 0;
2100 if (nNewTopLine >= (int)m_Screen2View.size())
2101 nNewTopLine = (int)m_Screen2View.size()-1;
2102 if (bAll)
2103 ScrollAllToLine(nNewTopLine);
2104 else
2105 ScrollToLine(nNewTopLine);
2108 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2110 return TRUE;
2113 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2115 if (CView::OnCreate(lpCreateStruct) == -1)
2116 return -1;
2118 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
2119 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2120 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2121 m_lfBaseFont.lfHeight = 0;
2122 m_lfBaseFont.lfWeight = FW_NORMAL;
2123 m_lfBaseFont.lfItalic = FALSE;
2124 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2125 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2126 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2127 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2128 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2130 return 0;
2133 void CBaseView::OnDestroy()
2135 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2137 m_pFindDialog->SendMessage(WM_CLOSE);
2138 return;
2140 CView::OnDestroy();
2141 DeleteFonts();
2142 ReleaseBitmap();
2145 void CBaseView::OnSize(UINT nType, int cx, int cy)
2147 CView::OnSize(nType, cx, cy);
2148 ReleaseBitmap();
2150 m_nScreenLines = -1;
2151 m_nScreenChars = -1;
2152 if (m_nLastScreenChars != GetScreenChars())
2154 BuildAllScreen2ViewVector();
2155 m_nLastScreenChars = m_nScreenChars;
2156 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2158 // if we're in wrap mode, the line wrapping most likely changed
2159 // and that means we have to redraw the whole window, not just the
2160 // scrolled part.
2161 Invalidate(FALSE);
2163 else
2165 // make sure the view header is redrawn
2166 CRect rcScroll;
2167 GetClientRect(&rcScroll);
2168 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2169 InvalidateRect(&rcScroll, FALSE);
2172 else
2174 // make sure the view header is redrawn
2175 CRect rcScroll;
2176 GetClientRect(&rcScroll);
2177 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2178 InvalidateRect(&rcScroll, FALSE);
2180 UpdateLocator();
2181 RecalcVertScrollBar();
2182 RecalcHorzScrollBar();
2184 UpdateCaret();
2187 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2189 if (m_pwndLeft)
2190 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2191 if (m_pwndRight)
2192 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2193 if (m_pwndBottom)
2194 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2195 if (m_pwndLocator)
2196 m_pwndLocator->Invalidate();
2197 return CView::OnMouseWheel(nFlags, zDelta, pt);
2200 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2202 if (m_pwndLeft)
2203 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2204 if (m_pwndRight)
2205 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2206 if (m_pwndBottom)
2207 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2208 if (m_pwndLocator)
2209 m_pwndLocator->Invalidate();
2212 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2214 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2215 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2217 if (bControl || bShift)
2219 if (m_pMainFrame->m_bWrapLines)
2220 return;
2221 // Ctrl-Wheel scrolls sideways
2222 ScrollSide(-zDelta/30);
2224 else
2226 ScrollVertical(zDelta);
2230 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2232 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2233 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2235 if (bControl || bShift)
2237 ScrollVertical(zDelta);
2239 else
2241 if (m_pMainFrame->m_bWrapLines)
2242 return;
2243 // Ctrl-Wheel scrolls sideways
2244 ScrollSide(-zDelta/30);
2248 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2250 if (nHitTest == HTCLIENT)
2252 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2254 if (m_nMouseLine < (int)m_Screen2View.size())
2256 if (m_nMouseLine >= 0)
2258 int viewLine = GetViewLineForScreen(m_nMouseLine);
2259 if (viewLine < m_pViewData->GetCount())
2261 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2263 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2264 return TRUE;
2270 if (m_mouseInMargin)
2272 ::SetCursor(m_margincursor);
2273 return TRUE;
2275 if (m_nMouseLine >= 0)
2277 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2278 return TRUE;
2281 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2282 return TRUE;
2284 return CView::OnSetCursor(pWnd, nHitTest, message);
2287 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2289 CView::OnKillFocus(pNewWnd);
2290 m_bFocused = FALSE;
2291 UpdateCaret();
2292 Invalidate();
2295 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2297 CView::OnSetFocus(pOldWnd);
2298 m_bFocused = TRUE;
2299 UpdateCaret();
2300 Invalidate();
2303 int CBaseView::GetLineFromPoint(CPoint point)
2305 ScreenToClient(&point);
2306 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2309 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2311 if (!this->IsWindowVisible())
2312 return;
2314 CIconMenu popup;
2315 if (!popup.CreatePopupMenu())
2316 return;
2318 AddContextItems(popup, state);
2320 CMenu popupEols;
2321 CMenu popupUnicode;
2322 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2323 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2324 if (IsWritable())
2326 CString temp;
2327 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2328 temp.LoadString(IDS_EDIT_TAB2SPACE);
2329 popup.AppendMenu((MF_STRING | oWhites.HasTabsToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2330 temp.LoadString(IDS_EDIT_SPACE2TAB);
2331 popup.AppendMenu((MF_STRING | oWhites.HasSpacesToConvert) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2332 temp.LoadString(IDS_EDIT_TRIM);
2333 popup.AppendMenu((MF_STRING | oWhites.HasTrailWhiteChars) ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2335 // add eol submenu
2336 if (!popupEols.CreatePopupMenu())
2337 return;
2339 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2340 for (int i = 1; i < _countof(eolArray); i++)
2342 CString temp = GetEolName(eolArray[i]);
2343 bool bChecked = (eEolType == eolArray[i]);
2344 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2347 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2348 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2350 // add encoding submenu
2351 if (!popupUnicode.CreatePopupMenu())
2352 return;
2353 for (int i = 0; i < _countof(uctArray); i++)
2355 CString temp = CFileTextLines::GetEncodingName(uctArray[i]);
2356 bool bChecked = (m_texttype == uctArray[i]);
2357 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2359 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2360 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2364 CompensateForKeyboard(point);
2366 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2367 ResetUndoStep();
2368 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2370 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2372 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2374 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2375 SaveUndoStep();
2377 switch (cmd)
2379 // 2-pane view commands; target is right view
2380 case POPUPCOMMAND_USELEFTBLOCK:
2381 m_pwndRight->UseLeftBlock();
2382 break;
2383 case POPUPCOMMAND_USELEFTFILE:
2384 m_pwndRight->UseLeftFile();
2385 break;
2386 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2387 m_pwndRight->UseBothLeftFirst();
2388 break;
2389 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2390 m_pwndRight->UseBothRightFirst();
2391 break;
2392 case POPUPCOMMAND_MARKBLOCK:
2393 m_pwndRight->MarkBlock(true);
2394 break;
2395 case POPUPCOMMAND_UNMARKBLOCK:
2396 m_pwndRight->MarkBlock(false);
2397 break;
2398 case POPUPCOMMAND_USELEFTFILEEXCEPTMARKED:
2399 m_pwndRight->UseLeftFileExceptMarked();
2400 break;
2401 // 2-pane view multiedit commands; target is left view
2402 case POPUPCOMMAND_PREPENDFROMRIGHT:
2403 if (!m_pwndLeft->IsReadonly())
2404 m_pwndLeft->UseBothRightFirst();
2405 break;
2406 case POPUPCOMMAND_REPLACEBYRIGHT:
2407 if (!m_pwndLeft->IsReadonly())
2408 m_pwndLeft->UseRightBlock();
2409 break;
2410 case POPUPCOMMAND_APPENDFROMRIGHT:
2411 if (!m_pwndLeft->IsReadonly())
2412 m_pwndLeft->UseBothLeftFirst();
2413 break;
2414 case POPUPCOMMAND_USERIGHTFILE:
2415 m_pwndLeft->UseRightFile();
2416 break;
2417 // 3-pane view commands; target is bottom view
2418 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2419 m_pwndBottom->UseBothRightFirst();
2420 break;
2421 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2422 m_pwndBottom->UseBothLeftFirst();
2423 break;
2424 case POPUPCOMMAND_USEYOURBLOCK:
2425 m_pwndBottom->UseRightBlock();
2426 break;
2427 case POPUPCOMMAND_USEYOURFILE:
2428 m_pwndBottom->UseRightFile();
2429 break;
2430 case POPUPCOMMAND_USETHEIRBLOCK:
2431 m_pwndBottom->UseLeftBlock();
2432 break;
2433 case POPUPCOMMAND_USETHEIRFILE:
2434 m_pwndBottom->UseLeftFile();
2435 break;
2436 // copy, cut and paste commands
2437 case ID_EDIT_COPY:
2438 OnEditCopy();
2439 break;
2440 case ID_EDIT_CUT:
2441 OnEditCut();
2442 break;
2443 case ID_EDIT_PASTE:
2444 OnEditPaste();
2445 break;
2446 // white chars manipulations
2447 case POPUPCOMMAND_TABTOSPACES:
2448 ConvertTabToSpaces();
2449 break;
2450 case POPUPCOMMAND_SPACESTOTABS:
2451 Tabularize();
2452 break;
2453 case POPUPCOMMAND_REMOVETRAILWHITES:
2454 RemoveTrailWhiteChars();
2455 break;
2456 default:
2457 return;
2458 } // switch (cmd)
2459 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2460 return;
2463 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2465 if (!m_pViewData)
2466 return;
2468 int nViewBlockStart = -1;
2469 int nViewBlockEnd = -1;
2470 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2471 if ((point.x >= 0) && (point.y >= 0))
2473 int nLine = GetLineFromPoint(point)-1;
2474 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2476 int nViewLine = GetViewLineForScreen(nLine);
2477 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2479 ClearSelection(); // Clear text-copy selection
2481 nViewBlockStart = nViewLine;
2482 nViewBlockEnd = nViewLine;
2483 DiffStates state = m_pViewData->GetState(nViewLine);
2484 while (nViewBlockStart > 0)
2486 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2487 if (!LinesInOneChange(-1, state, lineState))
2488 break;
2489 nViewBlockStart--;
2492 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2494 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2495 if (!LinesInOneChange(1, state, lineState))
2496 break;
2497 nViewBlockEnd++;
2500 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2501 UpdateCaretPosition(point);
2506 // FixSelection(); fix selection range
2507 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2508 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2510 DiffStates state = DIFFSTATE_UNKNOWN;
2511 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2513 // find a more 'relevant' state in the selection
2514 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2516 state = m_pViewData->GetState(i);
2517 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2518 break;
2521 OnContextMenu(point, state);
2524 void CBaseView::RefreshViews()
2526 if (m_pwndLeft)
2528 m_pwndLeft->UpdateStatusBar();
2529 m_pwndLeft->Invalidate();
2531 if (m_pwndRight)
2533 m_pwndRight->UpdateStatusBar();
2534 m_pwndRight->Invalidate();
2536 if (m_pwndBottom)
2538 m_pwndBottom->UpdateStatusBar();
2539 m_pwndBottom->Invalidate();
2541 if (m_pwndLocator)
2542 m_pwndLocator->Invalidate();
2545 void CBaseView::GoToFirstDifference()
2547 SetCaretToFirstViewLine();
2548 SelectNextBlock(1, false, false);
2551 void CBaseView::GoToFirstConflict()
2553 SetCaretToFirstViewLine();
2554 SelectNextBlock(1, true, false);
2557 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2559 ClearSelection();
2560 SetupAllSelection(nStart, max(nStart, nEnd));
2562 UpdateCaretPosition(SetupPoint(0, nStart));
2563 Invalidate();
2566 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2568 ClearSelection();
2569 SetupAllViewSelection(nStart, max(nStart, nEnd));
2571 UpdateCaretViewPosition(SetupPoint(0, nStart));
2572 Invalidate();
2575 void CBaseView::SetupAllViewSelection(int start, int end)
2577 SetupViewSelection(m_pwndBottom, start, end);
2578 SetupViewSelection(m_pwndLeft, start, end);
2579 SetupViewSelection(m_pwndRight, start, end);
2582 void CBaseView::SetupAllSelection(int start, int end)
2584 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2587 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2589 void CBaseView::SetupSelection(int start, int end)
2591 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2594 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2596 if (!IsViewGood(view))
2597 return;
2598 view->SetupViewSelection(start, end);
2601 void CBaseView::SetupViewSelection(int start, int end)
2603 // clear text selection before setting line selection ?
2604 m_nSelViewBlockStart = start;
2605 m_nSelViewBlockEnd = end;
2606 Invalidate();
2610 void CBaseView::OnMergePreviousconflict()
2612 SelectNextBlock(-1, true);
2615 void CBaseView::OnMergeNextconflict()
2617 SelectNextBlock(1, true);
2620 void CBaseView::OnMergeNextdifference()
2622 SelectNextBlock(1, false);
2625 void CBaseView::OnMergePreviousdifference()
2627 SelectNextBlock(-1, false);
2630 bool CBaseView::HasNextConflict()
2632 return SelectNextBlock(1, true, true, true);
2635 bool CBaseView::HasPrevConflict()
2637 return SelectNextBlock(-1, true, true, true);
2640 bool CBaseView::HasNextDiff()
2642 return SelectNextBlock(1, false, true, true);
2645 bool CBaseView::HasPrevDiff()
2647 return SelectNextBlock(-1, false, true, true);
2650 bool CBaseView::LinesInOneChange(int direction,
2651 DiffStates initialLineState, DiffStates currentLineState)
2653 // Checks whether all the adjacent lines starting from the initial line
2654 // and up to the current line form the single change
2656 // First of all, if the two lines have identical states, they surely
2657 // belong to one change.
2658 if (initialLineState == currentLineState)
2659 return true;
2661 // Either we move down and initial line state is "added" or "removed" and
2662 // current line state is "empty"...
2663 if (direction > 0)
2665 if (currentLineState == DIFFSTATE_EMPTY)
2667 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2668 return true;
2670 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2671 return true;
2673 // ...or we move up and initial line state is "empty" and current line
2674 // state is "added" or "removed".
2675 if (direction < 0)
2677 if (initialLineState == DIFFSTATE_EMPTY)
2679 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2680 return true;
2682 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2683 return true;
2685 return false;
2688 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2690 if (! m_pViewData)
2691 return false;
2693 const int linesCount = (int)m_Screen2View.size();
2694 if(linesCount == 0)
2695 return false;
2697 int nCenterPos = GetCaretPosition().y;
2698 int nLimit = -1;
2699 if (nDirection > 0)
2700 nLimit = linesCount;
2702 if (nCenterPos >= linesCount)
2703 nCenterPos = linesCount-1;
2705 if (bSkipEndOfCurrentBlock)
2707 // Find end of current block
2708 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2709 while (nCenterPos != nLimit)
2711 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2712 if (!LinesInOneChange(nDirection, state, lineState))
2713 break;
2714 nCenterPos += nDirection;
2718 // Find next diff/conflict block
2719 while (nCenterPos != nLimit)
2721 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2722 if (!bConflict &&
2723 (linestate != DIFFSTATE_NORMAL) &&
2724 (linestate != DIFFSTATE_UNKNOWN))
2726 break;
2728 if (bConflict &&
2729 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2730 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2731 (linestate == DIFFSTATE_CONFLICTED) ||
2732 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2734 break;
2737 nCenterPos += nDirection;
2739 if (nCenterPos == nLimit)
2740 return false;
2741 if (dryrun)
2742 return (nCenterPos != nLimit);
2744 // Find end of new block
2745 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2746 int nBlockEnd = nCenterPos;
2747 const int maxAllowedLine = nLimit-nDirection;
2748 while (nBlockEnd != maxAllowedLine)
2750 const int lineIndex = nBlockEnd + nDirection;
2751 if (lineIndex >= linesCount)
2752 break;
2753 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2754 if (!LinesInOneChange(nDirection, state, lineState))
2755 break;
2756 nBlockEnd += nDirection;
2759 int nTopPos = nCenterPos - (GetScreenLines()/2);
2760 if (nTopPos < 0)
2761 nTopPos = 0;
2763 POINT ptCaretPos = {0, nCenterPos};
2764 SetCaretPosition(ptCaretPos);
2765 ClearSelection();
2766 if (nDirection > 0)
2767 SetupAllSelection(nCenterPos, nBlockEnd);
2768 else
2769 SetupAllSelection(nBlockEnd, nCenterPos);
2771 ScrollAllToLine(nTopPos, FALSE);
2772 RecalcAllVertScrollBars(TRUE);
2773 SetCaretToLineStart();
2774 EnsureCaretVisible();
2775 OnNavigateNextinlinediff();
2777 UpdateViewsCaretPosition();
2778 UpdateCaret();
2779 ShowDiffLines(nCenterPos);
2780 return true;
2783 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2785 if (pNMHDR->idFrom != (UINT)m_hWnd)
2786 return FALSE;
2788 CString strTipText;
2789 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2791 DWORD pos = GetMessagePos();
2792 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2793 ScreenToClient(&point);
2794 const int nLine = GetButtonEventLineIndex(point);
2796 if (nLine >= 0)
2798 int nViewLine = GetViewLineForScreen(nLine);
2799 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2801 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2802 if (movedIndex >= 0)
2804 if (m_pViewData->IsMovedFrom(nViewLine))
2806 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2808 else
2810 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2817 *pResult = 0;
2818 if (strTipText.IsEmpty())
2819 return TRUE;
2821 // need to handle both ANSI and UNICODE versions of the message
2822 if (pNMHDR->code == TTN_NEEDTEXTA)
2824 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2825 pTTTA->lpszText = m_szTip;
2826 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2828 else
2830 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2831 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2832 pTTTW->lpszText = m_wszTip;
2835 return TRUE; // message was handled
2838 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2840 CRect rcClient;
2841 GetClientRect(rcClient);
2842 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2843 int marginwidth = MARGINWIDTH;
2844 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2846 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2848 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2850 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2852 // inside the header part of the view (showing the filename)
2853 pTI->hwnd = this->m_hWnd;
2854 this->GetClientRect(&pTI->rect);
2855 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2856 pTI->uId = (UINT)m_hWnd;
2857 pTI->lpszText = LPSTR_TEXTCALLBACK;
2859 // we want multi line tooltips
2860 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2861 if (pToolTip->GetSafeHwnd() != NULL)
2863 pToolTip->SetMaxTipWidth(INT_MAX);
2866 return (textrect.PtInRect(point) ? 1 : 2);
2869 return -1;
2872 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2874 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2875 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2877 switch (nChar)
2879 case VK_TAB:
2880 if (bControl)
2882 if (this==m_pwndLeft)
2884 if (IsViewGood(m_pwndRight))
2886 m_pwndRight->SetFocus();
2888 else if (IsViewGood(m_pwndBottom))
2890 m_pwndBottom->SetFocus();
2893 else if (this==m_pwndRight)
2895 if (IsViewGood(m_pwndBottom))
2897 m_pwndBottom->SetFocus();
2899 else if (IsViewGood(m_pwndLeft))
2901 m_pwndLeft->SetFocus();
2904 else if (this==m_pwndBottom)
2906 if (IsViewGood(m_pwndLeft))
2908 m_pwndLeft->SetFocus();
2910 else if (IsViewGood(m_pwndRight))
2912 m_pwndRight->SetFocus();
2916 break;
2917 case VK_PRIOR:
2919 POINT ptCaretPos = GetCaretPosition();
2920 ptCaretPos.y -= GetScreenLines();
2921 ptCaretPos.y = max(ptCaretPos.y, 0);
2922 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2923 SetCaretPosition(ptCaretPos);
2924 OnCaretMove(MOVELEFT, bShift);
2925 ShowDiffLines(ptCaretPos.y);
2927 break;
2928 case VK_NEXT:
2930 POINT ptCaretPos = GetCaretPosition();
2931 ptCaretPos.y += GetScreenLines();
2932 if (ptCaretPos.y >= GetLineCount())
2933 ptCaretPos.y = GetLineCount()-1;
2934 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2935 SetCaretPosition(ptCaretPos);
2936 OnCaretMove(MOVERIGHT, bShift);
2937 ShowDiffLines(ptCaretPos.y);
2939 break;
2940 case VK_HOME:
2942 if (bControl)
2944 ScrollAllToLine(0);
2945 SetCaretToViewStart();
2946 m_nCaretGoalPos = 0;
2947 if (bShift)
2948 AdjustSelection(MOVELEFT);
2949 else
2950 ClearSelection();
2951 UpdateCaret();
2953 else
2955 POINT ptCaretPos = GetCaretPosition();
2956 CString sLine = GetLineChars(ptCaretPos.y);
2957 int pos = 0;
2958 while (pos < sLine.GetLength())
2960 if (sLine[pos] != ' ' && sLine[pos] != '\t')
2961 break;
2962 ++pos;
2964 if (ptCaretPos.x == pos)
2966 SetCaretToLineStart();
2967 m_nCaretGoalPos = 0;
2968 OnCaretMove(MOVERIGHT, bShift);
2969 ScrollAllToChar(0);
2971 else
2973 ptCaretPos.x = pos;
2974 SetCaretAndGoalPosition(ptCaretPos);
2975 OnCaretMove(bShift);
2979 break;
2980 case VK_END:
2982 if (bControl)
2984 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2985 POINT ptCaretPos;
2986 ptCaretPos.y = GetLineCount()-1;
2987 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2988 SetCaretAndGoalPosition(ptCaretPos);
2989 if (bShift)
2990 AdjustSelection(MOVERIGHT);
2991 else
2992 ClearSelection();
2994 else
2996 POINT ptCaretPos = GetCaretPosition();
2997 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2998 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
3000 ptCaretPos.x--;
3002 SetCaretAndGoalPosition(ptCaretPos);
3003 OnCaretMove(bShift);
3006 break;
3007 case VK_BACK:
3008 if (IsWritable())
3010 if (! HasTextSelection())
3012 POINT ptCaretPos = GetCaretPosition();
3013 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
3014 break;
3015 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3016 if (bControl)
3017 MoveCaretWordLeft();
3018 else
3020 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3024 m_ptSelectionViewPosStart = GetCaretViewPosition();
3026 RemoveSelectedText();
3028 break;
3029 case VK_DELETE:
3030 if (IsWritable())
3032 if (! HasTextSelection())
3034 if (bControl)
3036 m_ptSelectionViewPosStart = GetCaretViewPosition();
3037 MoveCaretWordRight();
3038 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3040 else
3042 if (! MoveCaretRight())
3043 break;
3044 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3045 MoveCaretLeft();
3046 m_ptSelectionViewPosStart = GetCaretViewPosition();
3049 RemoveSelectedText();
3051 break;
3052 case VK_INSERT:
3053 m_bInsertMode = !m_bInsertMode;
3054 UpdateCaret();
3055 break;
3057 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3060 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3062 const int nClickedLine = GetButtonEventLineIndex(point);
3063 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3065 POINT ptCaretPos;
3066 ptCaretPos.y = nClickedLine;
3067 int xpos2 = CalcColFromPoint(point.x, nClickedLine);
3068 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3069 SetCaretAndGoalPosition(ptCaretPos);
3071 if (nFlags & MK_SHIFT)
3072 AdjustSelection(MOVERIGHT);
3073 else
3075 ClearSelection();
3076 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3077 if (point.x < GetMarginWidth())
3079 // select the whole line
3080 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3081 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3085 UpdateViewsCaretPosition();
3086 Invalidate();
3089 CView::OnLButtonDown(nFlags, point);
3092 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3094 if (zChar == ' ' || zChar == '\t' )
3096 return CHG_WHITESPACE;
3098 if (zChar < 0x20)
3100 return CHG_CONTROL;
3102 if (m_sWordSeparators.Find(zChar) >= 0)
3104 return CHG_WORDSEPARATOR;
3106 return CHG_WORDLETTER;
3109 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3111 if (m_pViewData == 0) {
3112 CView::OnLButtonDblClk(nFlags, point);
3113 return;
3116 const int nClickedLine = GetButtonEventLineIndex(point);
3117 if ( nClickedLine < 0)
3118 return;
3119 int nViewLine = GetViewLineForScreen(nClickedLine);
3120 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3122 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3124 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3126 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3127 int screenLine = FindViewLineNumber(movedindex);
3128 int nTop = screenLine - GetScreenLines()/2;
3129 if (nTop < 0)
3130 nTop = 0;
3131 ScrollAllToLine(nTop);
3132 // find and select the whole moved block
3133 int startSel = movedindex;
3134 int endSel = movedindex;
3135 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3136 startSel--;
3137 startSel++;
3138 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3139 endSel++;
3140 endSel--;
3141 m_pOtherView->SetupSelection(startSel, endSel);
3142 return CView::OnLButtonDblClk(nFlags, point);
3146 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3148 // a double click on a marker expands the hidden text
3149 int i = nViewLine;
3150 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3152 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3153 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3154 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3155 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3156 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3157 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3158 i++;
3160 BuildAllScreen2ViewVector();
3161 if (m_pwndLeft)
3162 m_pwndLeft->Invalidate();
3163 if (m_pwndRight)
3164 m_pwndRight->Invalidate();
3165 if (m_pwndBottom)
3166 m_pwndBottom->Invalidate();
3168 else
3170 POINT ptCaretPos;
3171 ptCaretPos.y = nClickedLine;
3172 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3173 SetCaretPosition(ptCaretPos);
3174 ClearSelection();
3176 POINT ptViewCarret = GetCaretViewPosition();
3177 nViewLine = ptViewCarret.y;
3178 if (nViewLine >= GetViewCount())
3179 return;
3180 const CString &sLine = GetViewLine(nViewLine);
3181 int nLineLength = sLine.GetLength();
3182 int nBasePos = ptViewCarret.x;
3183 // get target char group
3184 ECharGroup eLeft = CHG_UNKNOWN;
3185 if (nBasePos > 0)
3187 eLeft = GetCharGroup(sLine[nBasePos-1]);
3189 ECharGroup eRight = CHG_UNKNOWN;
3190 if (nBasePos < nLineLength)
3192 eRight = GetCharGroup(sLine[nBasePos]);
3194 ECharGroup eTarget = max(eRight, eLeft);
3195 // find left margin
3196 int nLeft = nBasePos;
3197 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3199 nLeft--;
3201 // get right margin
3202 int nRight = nBasePos;
3203 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3205 nRight++;
3207 // set selection
3208 m_ptSelectionViewPosStart.x = nLeft;
3209 m_ptSelectionViewPosStart.y = nViewLine;
3210 m_ptSelectionViewPosEnd.x = nRight;
3211 m_ptSelectionViewPosEnd.y = nViewLine;
3212 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3213 SetupAllViewSelection(nViewLine, nViewLine);
3214 // set caret
3215 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3216 UpdateViewsCaretPosition();
3217 UpdateGoalPos();
3219 // set mark word
3220 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3221 int nMarkWidth = max(nRight - nLeft, 0);
3222 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3223 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3225 m_sMarkedWord.Empty();
3228 if (m_pwndLeft)
3229 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3230 if (m_pwndRight)
3231 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3232 if (m_pwndBottom)
3233 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3235 Invalidate();
3236 if (m_pwndLocator)
3237 m_pwndLocator->Invalidate();
3240 CView::OnLButtonDblClk(nFlags, point);
3243 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3245 const int nClickedLine = GetButtonEventLineIndex(point);
3246 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3248 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3250 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3251 if (pidl)
3253 SHOpenFolderAndSelectItems(pidl,0,0,0);
3254 CoTaskMemFree((LPVOID)pidl);
3257 return;
3259 POINT ptCaretPos;
3260 ptCaretPos.y = nClickedLine;
3261 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3262 SetCaretAndGoalPosition(ptCaretPos);
3263 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3264 if (m_pwndLeft)
3265 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3266 if (m_pwndRight)
3267 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3268 if (m_pwndBottom)
3269 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3270 ClearSelection();
3271 m_ptSelectionViewPosStart.x = 0;
3272 m_ptSelectionViewPosStart.y = nClickedLine;
3273 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3274 m_ptSelectionViewPosEnd.y = nClickedLine;
3275 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3276 UpdateViewsCaretPosition();
3277 Invalidate();
3278 if (m_pwndLocator)
3279 m_pwndLocator->Invalidate();
3282 void CBaseView::OnEditCopy()
3284 CString sCopyData = GetSelectedText();
3286 if (!sCopyData.IsEmpty())
3288 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3292 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3294 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3296 --m_pMainFrame->m_nMoveMovesToIgnore;
3297 CView::OnMouseMove(nFlags, point);
3298 return;
3300 int nMouseLine = GetButtonEventLineIndex(point);
3301 if (nMouseLine < -1)
3302 nMouseLine = -1;
3303 m_mouseInMargin = point.x < GetMarginWidth();
3305 ShowDiffLines(nMouseLine);
3307 KillTimer(IDT_SCROLLTIMER);
3308 if (nFlags & MK_LBUTTON)
3310 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3311 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3312 if (saveMouseLine < 0)
3313 return;
3314 int col = CalcColFromPoint(point.x, saveMouseLine);
3315 int charIndex = CalculateCharIndex(saveMouseLine, col);
3316 if (HasSelection() &&
3317 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3319 POINT ptCaretPos = {charIndex, nMouseLine};
3320 SetCaretAndGoalPosition(ptCaretPos);
3321 AdjustSelection(MOVERIGHT);
3322 Invalidate();
3323 UpdateWindow();
3325 if (nMouseLine < m_nTopLine)
3327 ScrollAllToLine(m_nTopLine-1, TRUE);
3328 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3330 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3332 ScrollAllToLine(m_nTopLine+1, TRUE);
3333 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3335 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3337 ScrollAllSide(-1);
3338 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3340 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3342 ScrollAllSide(1);
3343 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3345 SetCapture();
3349 CView::OnMouseMove(nFlags, point);
3352 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3354 ShowDiffLines(-1);
3355 ReleaseCapture();
3356 KillTimer(IDT_SCROLLTIMER);
3358 __super::OnLButtonUp(nFlags, point);
3361 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3363 if (nIDEvent == IDT_SCROLLTIMER)
3365 POINT point;
3366 GetCursorPos(&point);
3367 ScreenToClient(&point);
3368 int nMouseLine = GetButtonEventLineIndex(point);
3369 if (nMouseLine < -1)
3371 nMouseLine = -1;
3373 if (GetKeyState(VK_LBUTTON)&0x8000)
3375 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3376 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3377 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3378 if (nMouseLine < m_nTopLine)
3380 ScrollAllToLine(m_nTopLine-1, TRUE);
3381 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3383 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3385 ScrollAllToLine(m_nTopLine+1, TRUE);
3386 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3388 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3390 ScrollAllSide(-1);
3391 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3393 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3395 ScrollAllSide(1);
3396 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3402 CView::OnTimer(nIDEvent);
3405 void CBaseView::ShowDiffLines(int nLine)
3407 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3409 m_pwndLineDiffBar->ShowLines(nLine);
3410 nLine = -1;
3411 m_nMouseLine = nLine;
3412 return;
3415 if ((!m_pwndRight)||(!m_pwndLeft))
3416 return;
3417 if(m_pMainFrame->m_bOneWay)
3418 return;
3420 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3421 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3423 if (nLine < 0)
3424 return;
3426 if (nLine != m_nMouseLine)
3428 if (nLine >= GetLineCount())
3429 nLine = -1;
3430 m_nMouseLine = nLine;
3431 m_pwndLineDiffBar->ShowLines(nLine);
3433 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3436 const viewdata& CBaseView::GetEmptyLineData()
3438 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3439 return emptyLine;
3442 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3444 for (int i = 0; i < nCount; i++)
3446 InsertViewData(nFirstView, GetEmptyLineData());
3451 void CBaseView::UpdateCaret()
3453 POINT ptCaretPos = GetCaretPosition();
3454 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3455 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3456 SetCaretPosition(ptCaretPos);
3458 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3460 if (m_bFocused &&
3461 ptCaretPos.y >= m_nTopLine &&
3462 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3463 nCaretOffset >= m_nOffsetChar &&
3464 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3466 POINT pt1 = TextToClient(ptCaretPos);
3467 if (m_bInsertMode)
3468 CreateSolidCaret(2, GetLineHeight());
3469 else
3471 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3472 POINT pt2 = TextToClient(pt);
3473 int width = max(GetCharWidth(), pt2.x - pt1.x);
3474 CreateSolidCaret(width, GetLineHeight());
3476 SetCaretPos(pt1);
3477 ShowCaret();
3479 else
3481 HideCaret();
3485 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3487 POINT ptViewPos;
3488 ptViewPos.x = pt.x;
3490 int nSubLine = GetSubLineOffset(pt.y);
3491 if (nSubLine > 0)
3493 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3495 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3499 ptViewPos.y = GetViewLineForScreen(pt.y);
3500 return ptViewPos;
3503 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3505 POINT ptPos;
3506 int nViewLineLenLeft = GetViewLineLength(pt.y);
3507 ptPos.x = min(nViewLineLenLeft, pt.x);
3508 ptPos.y = FindScreenLineForViewLine(pt.y);
3509 if (GetViewLineForScreen(ptPos.y) != pt.y )
3511 ptPos.x = 0;
3513 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3515 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3516 while (nSubLineLength < ptPos.x)
3518 ptPos.x -= nSubLineLength;
3519 nViewLineLenLeft -= nSubLineLength;
3520 ptPos.y++;
3521 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3523 // last pos of non last sub-line go to start of next screen line
3524 // Note: while this works correctly, it's not what a user might expect:
3525 // cursor-right when the caret is before the last char of a wrapped line
3526 // now moves the caret to the next line. But users expect the caret to
3527 // move to the right of the last char instead, and with another cursor-right
3528 // keystroke to move the caret to the next line.
3529 // Basically, this would require to handle two caret positions for the same
3530 // logical position in the line string (one on the last position of the first line,
3531 // one on the first position of the new line. For non-wrapped lines this works
3532 // because there's an 'invisible' newline char at the end of the first line.
3533 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3535 ptPos.x = 0;
3536 ptPos.y++;
3540 return ptPos;
3544 void CBaseView::EnsureCaretVisible()
3546 POINT ptCaretPos = GetCaretPosition();
3547 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3549 if (ptCaretPos.y < m_nTopLine)
3550 ScrollAllToLine(ptCaretPos.y);
3551 int screnLines = GetScreenLines();
3552 if (screnLines)
3554 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3555 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3556 if (nCaretOffset < m_nOffsetChar)
3557 ScrollAllToChar(nCaretOffset);
3558 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3559 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3563 int CBaseView::CalculateActualOffset(const POINT& point)
3565 int nLineIndex = point.y;
3566 int nCharIndex = point.x;
3567 ASSERT(nCharIndex >= 0);
3568 CString sLine = GetLineChars(nLineIndex);
3569 int nLineLength = sLine.GetLength();
3570 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3573 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3575 int nLength = GetLineLength(nLineIndex);
3576 int nSubLine = GetSubLineOffset(nLineIndex);
3577 if (nSubLine>=0)
3579 int nViewLine = GetViewLineForScreen(nLineIndex);
3580 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3582 int nMultilineCount = CountMultiLines(nViewLine);
3583 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3585 nLength--;
3589 CString Line = GetLineChars(nLineIndex);
3590 int nIndex = 0;
3591 int nOffset = 0;
3592 int nTabSize = GetTabSize();
3593 while (nOffset < nActualOffset && nIndex < nLength)
3595 if (Line.GetAt(nIndex) == _T('\t'))
3596 nOffset += (nTabSize - nOffset % nTabSize);
3597 else
3598 ++nOffset;
3599 ++nIndex;
3601 return nIndex;
3605 * @param xpos X coordinate in CBaseView
3606 * @param lineIndex logical line index (e.g. wrap/collapse)
3608 int CBaseView::CalcColFromPoint(int xpos, int lineIndex)
3610 int xpos2;
3611 CDC *pDC = GetDC();
3612 if (pDC != nullptr)
3614 CString text = ExpandChars(GetLineChars(lineIndex), 0);
3615 int fit = text.GetLength();
3616 std::unique_ptr<int> posBuffer(new int[fit]);
3617 pDC->SelectObject(GetFont()); // is this right font ?
3618 SIZE size;
3619 GetTextExtentExPoint(pDC->GetSafeHdc(), text, fit, INT_MAX, &fit, posBuffer.get(), &size);
3620 ReleaseDC(pDC);
3621 int lower = -1, upper = fit - 1;
3622 int xcheck = xpos - GetMarginWidth() + m_nOffsetChar * GetCharWidth();
3625 int middle = (upper + lower + 1) / 2;
3626 int width = posBuffer.get()[middle];
3627 if (xcheck < width)
3628 upper = middle - 1;
3629 else
3630 lower = middle;
3631 } while (lower < upper);
3632 lower++;
3633 xpos2 = lower;
3634 if (lower < fit - 1)
3636 int charWidth = posBuffer.get()[lower] - (lower > 0 ? posBuffer.get()[lower - 1] : 0);
3637 if (posBuffer.get()[lower] - xcheck <= charWidth / 2)
3638 xpos2++;
3641 else
3643 xpos2 = (xpos - GetMarginWidth()) / GetCharWidth() + m_nOffsetChar;
3644 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3645 xpos2++;
3647 return xpos2;
3650 POINT CBaseView::TextToClient(const POINT& point)
3652 POINT pt;
3653 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3654 pt.y = nOffsetScreenLine * GetLineHeight();
3655 pt.x = CalculateActualOffset(point);
3657 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3658 CDC * pDC = GetDC();
3659 if (pDC)
3661 pDC->SelectObject(GetFont()); // is this right font ?
3662 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3663 CString sLine = GetLineChars(nScreenLine);
3664 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3665 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3666 ReleaseDC(pDC);
3667 } else {
3668 nLeft += pt.x * GetCharWidth();
3671 pt.x = nLeft;
3672 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3673 return pt;
3676 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3678 CView::OnChar(nChar, nRepCnt, nFlags);
3680 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3681 bool bSkipSelectionClear = false;
3683 if (IsReadonly())
3684 return;
3686 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3687 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3689 return;
3692 if (!m_pViewData) // no data - nothing to do
3693 return;
3695 if (nChar == VK_F16)
3697 // generated by a ctrl+backspace - ignore.
3699 else if (nChar==VK_TAB && HasTextLineSelection())
3701 // change indentation for selected lines
3702 if (bShift)
3704 RemoveIndentationForSelectedBlock();
3706 else
3708 AddIndentationForSelectedBlock();
3710 bSkipSelectionClear = true;
3712 else if ((nChar > 31)||(nChar == VK_TAB))
3714 ResetUndoStep();
3715 RemoveSelectedText();
3716 POINT ptCaretViewPos = GetCaretViewPosition();
3717 int nViewLine = ptCaretViewPos.y;
3718 if ((nViewLine==0)&&(GetViewCount()==0))
3719 OnChar(VK_RETURN, 0, 0);
3720 int charCount = 1;
3721 viewdata lineData = GetViewData(nViewLine);
3722 if (nChar == VK_TAB)
3724 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3725 if (indentChars > 0)
3727 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3728 charCount = indentChars;
3730 else
3731 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3733 else
3735 if (m_bInsertMode)
3736 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3737 else
3739 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3740 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3741 else
3742 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3745 if (IsStateEmpty(lineData.state))
3747 // if not last line set EOL
3748 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3750 if (!IsViewLineEmpty(nCheckViewLine))
3752 lineData.ending = m_lineendings;
3753 break;
3756 // make sure previous (non empty) line have EOL set
3757 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3759 if (!IsViewLineEmpty(nCheckViewLine))
3761 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3763 SetViewLineEnding(nCheckViewLine, m_lineendings);
3765 break;
3769 lineData.state = DIFFSTATE_EDITED;
3770 bool bNeedRenumber = false;
3771 if (lineData.linenumber == -1)
3773 lineData.linenumber = 0;
3774 bNeedRenumber = true;
3776 SetViewData(nViewLine, lineData);
3777 SetModified();
3778 SaveUndoStep();
3779 BuildAllScreen2ViewVector(nViewLine);
3780 if (bNeedRenumber)
3782 UpdateViewLineNumbers();
3784 for (int i = 0; i < charCount; ++i)
3785 MoveCaretRight();
3786 UpdateGoalPos();
3788 else if (nChar == 10)
3790 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3791 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3792 EOL newEOL = EOL_CRLF;
3793 switch (eol)
3795 case EOL_CRLF:
3796 newEOL = EOL_CR;
3797 break;
3798 case EOL_CR:
3799 newEOL = EOL_LF;
3800 break;
3801 case EOL_LF:
3802 newEOL = EOL_CRLF;
3803 break;
3805 if (eol==EOL_NOENDING || eol==newEOL)
3806 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3807 // to add EOL on newly edited empty line hit enter
3808 // don't store into UNDO if no change happened
3809 // and don't mark file as modified
3810 return;
3811 AddUndoViewLine(nViewLine);
3812 m_pViewData->SetLineEnding(nViewLine, newEOL);
3813 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3814 UpdateGoalPos();
3816 else if (nChar == VK_RETURN)
3818 // insert a new, fresh and empty line below the cursor
3819 RemoveSelectedText();
3821 CUndo::GetInstance().BeginGrouping();
3823 POINT ptCaretViewPos = GetCaretViewPosition();
3824 int nViewLine = ptCaretViewPos.y;
3825 int nLeft = ptCaretViewPos.x;
3826 CString sLine = GetViewLineChars(nViewLine);
3827 CString sLineLeft = sLine.Left(nLeft);
3828 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3829 EOL eOriginalEnding = EOL_AUTOLINE;
3830 if (m_pViewData->GetCount() > nViewLine)
3831 eOriginalEnding = GetViewLineEnding(nViewLine);
3833 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3835 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3836 SetViewData(nViewLine, newFirstLine);
3839 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3840 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3841 InsertViewData(nInsertLine, newLastLine);
3842 SetModified();
3843 SaveUndoStep();
3845 // adds new line everywhere except me
3846 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3848 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3850 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3852 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3854 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3856 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3858 SaveUndoStep();
3860 UpdateViewLineNumbers();
3861 SaveUndoStep();
3862 CUndo::GetInstance().EndGrouping();
3864 BuildAllScreen2ViewVector();
3865 // move the cursor to the new line
3866 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3867 SetCaretAndGoalViewPosition(ptCaretViewPos);
3869 else
3870 return; // Unknown control character -- ignore it.
3871 if (!bSkipSelectionClear)
3872 ClearSelection();
3873 EnsureCaretVisible();
3874 UpdateCaret();
3875 Invalidate(FALSE);
3878 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3880 ResetUndoStep();
3881 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3882 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3883 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3884 SetModified();
3885 SaveUndoStep();
3886 RecalcAllVertScrollBars();
3887 Invalidate(FALSE);
3890 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3892 if (m_pViewData == NULL)
3893 return;
3894 int viewLine = nViewLineIndex;
3895 EOL ending = m_pViewData->GetLineEnding(viewLine);
3896 if (ending == EOL_NOENDING)
3898 ending = m_lineendings;
3900 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3901 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3903 CString sPartLine = GetViewLineChars(nViewLineIndex);
3904 int nPosx = GetCaretPosition().x; // should be view pos ?
3905 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3906 sPartLine = sPartLine.Mid(nPosx);
3907 newLine.sLine = sPartLine;
3909 m_pViewData->InsertData(viewLine+1, newLine);
3910 BuildAllScreen2ViewVector();
3913 void CBaseView::RemoveSelectedText()
3915 if (m_pViewData == NULL)
3916 return;
3917 if (!HasTextSelection())
3918 return;
3920 // fix selection if starts or ends on empty line
3921 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3922 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3925 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3926 SetCaretViewPosition(m_ptSelectionViewPosStart);
3927 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3930 m_ptSelectionViewPosStart = GetCaretViewPosition();
3931 if (!HasTextSelection())
3933 ClearSelection();
3934 return;
3937 // We want to undo the insertion in a single step.
3938 ResetUndoStep();
3939 CUndo::GetInstance().BeginGrouping();
3941 // combine first and last line
3942 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3943 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3944 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3945 oFirstLine.ending = oLastLine.ending;
3946 oFirstLine.state = DIFFSTATE_EDITED;
3947 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3949 // clean up middle lines if any
3950 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3952 viewdata oEmptyLine = GetEmptyLineData();
3953 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3955 SetViewData(nViewLine, oEmptyLine);
3957 SaveUndoStep();
3959 if (CleanEmptyLines())
3961 BuildAllScreen2ViewVector(); // schedule full rebuild
3963 SaveUndoStep();
3964 UpdateViewLineNumbers();
3967 SetModified(); //TODO set modified only if real data was changed
3968 SaveUndoStep();
3969 CUndo::GetInstance().EndGrouping();
3971 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3972 SetCaretViewPosition(m_ptSelectionViewPosStart);
3973 UpdateGoalPos();
3974 ClearSelection();
3975 UpdateCaret();
3976 EnsureCaretVisible();
3977 Invalidate(FALSE);
3980 void CBaseView::PasteText()
3982 if (!OpenClipboard())
3983 return;
3985 CString sClipboardText;
3986 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3987 if (hglb)
3989 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3990 sClipboardText = CString(lpstr);
3991 GlobalUnlock(hglb);
3993 hglb = GetClipboardData(CF_UNICODETEXT);
3994 if (hglb)
3996 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3997 sClipboardText = lpstr;
3998 GlobalUnlock(hglb);
4000 CloseClipboard();
4002 if (sClipboardText.IsEmpty())
4003 return;
4005 sClipboardText.Replace(_T("\r\n"), _T("\r"));
4006 sClipboardText.Replace('\n', '\r');
4008 InsertText(sClipboardText);
4011 void CBaseView::OnCaretDown()
4013 POINT ptCaretPos = GetCaretPosition();
4014 int nLine = ptCaretPos.y;
4015 int nNextLine = nLine + 1;
4016 if (nNextLine >= GetLineCount()) // already at last line
4018 return;
4021 POINT ptCaretViewPos = GetCaretViewPosition();
4022 int nViewLine = ptCaretViewPos.y;
4023 int nNextViewLine = GetViewLineForScreen(nNextLine);
4024 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4026 // find next suitable screen line
4027 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4029 nNextLine++;
4030 if (nNextLine >= GetLineCount())
4032 return;
4034 nNextViewLine = GetViewLineForScreen(nNextLine);
4037 ptCaretPos.y = nNextLine;
4038 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4039 SetCaretPosition(ptCaretPos);
4040 OnCaretMove(MOVELEFT);
4041 ShowDiffLines(ptCaretPos.y);
4044 bool CBaseView::MoveCaretLeft()
4046 POINT ptCaretViewPos = GetCaretViewPosition();
4048 //int nViewLine = ptCaretViewPos.y;
4049 if (ptCaretViewPos.x == 0)
4051 int nPrevLine = GetCaretPosition().y;
4052 int nPrevViewLine;
4053 do {
4054 nPrevLine--;
4055 if (nPrevLine < 0)
4057 return false;
4059 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4060 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4061 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4062 ShowDiffLines(nPrevLine);
4064 else
4065 --ptCaretViewPos.x;
4067 SetCaretAndGoalViewPosition(ptCaretViewPos);
4068 return true;
4071 bool CBaseView::MoveCaretRight()
4073 POINT ptCaretViewPos = GetCaretViewPosition();
4075 int nViewLine = ptCaretViewPos.y;
4076 int nViewLineLen = GetViewLineLength(nViewLine);
4077 if (ptCaretViewPos.x >= nViewLineLen)
4079 int nNextLine = GetCaretPosition().y;
4080 int nNextViewLine;
4081 do {
4082 nNextLine++;
4083 if (nNextLine >= GetLineCount())
4085 return false;
4087 nNextViewLine = GetViewLineForScreen(nNextLine);
4088 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4089 ptCaretViewPos.y = nNextViewLine;
4090 ptCaretViewPos.x = 0;
4091 ShowDiffLines(nNextLine);
4093 else
4094 ++ptCaretViewPos.x;
4096 SetCaretAndGoalViewPosition(ptCaretViewPos);
4097 return true;
4100 void CBaseView::UpdateGoalPos()
4102 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4105 void CBaseView::OnCaretLeft()
4107 MoveCaretLeft();
4108 OnCaretMove(MOVELEFT);
4111 void CBaseView::OnCaretRight()
4113 MoveCaretRight();
4114 OnCaretMove(MOVERIGHT);
4117 void CBaseView::OnCaretUp()
4119 POINT ptCaretPos = GetCaretPosition();
4120 int nLine = ptCaretPos.y;
4121 if (nLine <= 0) // already at first line
4123 return;
4125 int nPrevLine = nLine - 1;
4127 POINT ptCaretViewPos = GetCaretViewPosition();
4128 int nViewLine = ptCaretViewPos.y;
4129 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4130 if (nPrevViewLine != nViewLine) // not on same view line
4132 // find previous suitable screen line
4133 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4135 if (nPrevLine <= 0)
4137 return;
4139 nPrevLine--;
4140 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4143 ptCaretPos.y = nPrevLine;
4144 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4145 SetCaretPosition(ptCaretPos);
4146 OnCaretMove(MOVELEFT);
4147 ShowDiffLines(ptCaretPos.y);
4150 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4152 switch (GetCharGroup(ch))
4154 case CHG_CONTROL:
4155 case CHG_WHITESPACE:
4156 case CHG_WORDSEPARATOR:
4157 return true;
4159 return false;
4162 bool CBaseView::IsCaretAtWordBoundary()
4164 POINT ptViewCaret = GetCaretViewPosition();
4165 CString line = GetViewLineChars(ptViewCaret.y);
4166 if (line.IsEmpty())
4167 return false; // no boundary at the empty lines
4168 if (ptViewCaret.x == 0)
4169 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4170 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4171 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4172 return
4173 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4174 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4177 void CBaseView::UpdateViewsCaretPosition()
4179 POINT ptCaretPos = GetCaretPosition();
4180 if (m_pwndBottom && m_pwndBottom!=this)
4181 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4182 if (m_pwndLeft && m_pwndLeft!=this)
4183 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4184 if (m_pwndRight && m_pwndRight!=this)
4185 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4188 void CBaseView::OnCaretWordleft()
4190 MoveCaretWordLeft();
4191 OnCaretMove(MOVELEFT);
4194 void CBaseView::OnCaretWordright()
4196 MoveCaretWordRight();
4197 OnCaretMove(MOVERIGHT);
4200 void CBaseView::MoveCaretWordLeft()
4202 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4207 void CBaseView::MoveCaretWordRight()
4209 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4214 void CBaseView::ClearCurrentSelection()
4216 m_ptSelectionViewPosStart = GetCaretViewPosition();
4217 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4218 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4219 m_nSelViewBlockStart = -1;
4220 m_nSelViewBlockEnd = -1;
4221 Invalidate(FALSE);
4224 void CBaseView::ClearSelection()
4226 if (m_pwndLeft)
4227 m_pwndLeft->ClearCurrentSelection();
4228 if (m_pwndRight)
4229 m_pwndRight->ClearCurrentSelection();
4230 if (m_pwndBottom)
4231 m_pwndBottom->ClearCurrentSelection();
4234 void CBaseView::AdjustSelection(bool bMoveLeft)
4236 POINT ptCaretViewPos = GetCaretViewPosition();
4237 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4239 // select all have been used recently update origin
4240 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4242 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4243 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4245 m_ptSelectionViewPosStart = ptCaretViewPos;
4246 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4248 else
4250 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4251 m_ptSelectionViewPosEnd = ptCaretViewPos;
4254 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4256 Invalidate(FALSE);
4259 void CBaseView::OnEditCut()
4261 if (IsWritable())
4263 OnEditCopy();
4264 RemoveSelectedText();
4268 void CBaseView::OnEditPaste()
4270 if (IsWritable())
4272 CUndo::GetInstance().BeginGrouping();
4273 RemoveSelectedText();
4274 PasteText();
4275 CUndo::GetInstance().EndGrouping();
4279 void CBaseView::DeleteFonts()
4281 for (int i=0; i<fontsCount; i++)
4283 if (m_apFonts[i] != NULL)
4285 m_apFonts[i]->DeleteObject();
4286 delete m_apFonts[i];
4287 m_apFonts[i] = NULL;
4292 void CBaseView::OnCaretMove(bool bMoveLeft)
4294 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4295 OnCaretMove(bMoveLeft, bShift);
4298 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4300 if(isShiftPressed)
4301 AdjustSelection(bMoveLeft);
4302 else
4303 ClearSelection();
4304 EnsureCaretVisible();
4305 UpdateCaret();
4308 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4310 AddCutCopyAndPaste(popup);
4313 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4315 popup.AppendMenu(MF_SEPARATOR, NULL);
4316 CString temp;
4317 temp.LoadString(IDS_EDIT_COPY);
4318 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4319 if (IsWritable())
4321 temp.LoadString(IDS_EDIT_CUT);
4322 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4323 temp.LoadString(IDS_EDIT_PASTE);
4324 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4325 popup.AppendMenu(MF_SEPARATOR, NULL);
4329 void CBaseView::CompensateForKeyboard(CPoint& point)
4331 // if the context menu is invoked through the keyboard, we have to use
4332 // a calculated position on where to anchor the menu on
4333 if (ArePointsSame(point, SetupPoint(-1, -1)))
4335 CRect rect;
4336 GetWindowRect(&rect);
4337 point = rect.CenterPoint();
4341 HICON CBaseView::LoadIcon(WORD iconId)
4343 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4344 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4345 return (HICON)icon;
4348 void CBaseView::ReleaseBitmap()
4350 if (m_pCacheBitmap != NULL)
4352 m_pCacheBitmap->DeleteObject();
4353 delete m_pCacheBitmap;
4354 m_pCacheBitmap = NULL;
4358 void CBaseView::BuildMarkedWordArray()
4360 int lineCount = GetLineCount();
4361 m_arMarkedWordLines.clear();
4362 m_arMarkedWordLines.reserve(lineCount);
4363 bool bDoit = !m_sMarkedWord.IsEmpty();
4364 for (int i = 0; i < lineCount; ++i)
4366 if (bDoit)
4368 CString line = GetLineChars(i);
4370 if (!line.IsEmpty())
4372 int found = 0;
4373 int nMarkStart = -1;
4374 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4376 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4377 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4378 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4379 if (eLeft != eStart)
4381 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4382 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4383 if (eRight != eEnd)
4385 found = 1;
4386 break;
4390 m_arMarkedWordLines.push_back(found);
4392 else
4393 m_arMarkedWordLines.push_back(0);
4395 else
4396 m_arMarkedWordLines.push_back(0);
4400 void CBaseView::BuildFindStringArray()
4402 int lineCount = GetLineCount();
4403 m_arFindStringLines.clear();
4404 m_arFindStringLines.reserve(lineCount);
4405 bool bDoit = !m_sFindText.IsEmpty();
4406 int s = 0;
4407 int e = 0;
4408 for (int i = 0; i < lineCount; ++i)
4410 if (bDoit)
4412 CString line = GetLineChars(i);
4414 if (!line.IsEmpty())
4416 switch (m_pViewData->GetState(i))
4418 case DIFFSTATE_EMPTY:
4419 m_arFindStringLines.push_back(0);
4420 break;
4421 case DIFFSTATE_UNKNOWN:
4422 case DIFFSTATE_NORMAL:
4423 if (m_bLimitToDiff)
4425 m_arFindStringLines.push_back(0);
4426 break;
4428 case DIFFSTATE_REMOVED:
4429 case DIFFSTATE_REMOVEDWHITESPACE:
4430 case DIFFSTATE_ADDED:
4431 case DIFFSTATE_ADDEDWHITESPACE:
4432 case DIFFSTATE_WHITESPACE:
4433 case DIFFSTATE_WHITESPACE_DIFF:
4434 case DIFFSTATE_CONFLICTED:
4435 case DIFFSTATE_CONFLICTED_IGNORED:
4436 case DIFFSTATE_CONFLICTADDED:
4437 case DIFFSTATE_CONFLICTEMPTY:
4438 case DIFFSTATE_CONFLICTRESOLVED:
4439 case DIFFSTATE_IDENTICALREMOVED:
4440 case DIFFSTATE_IDENTICALADDED:
4441 case DIFFSTATE_THEIRSREMOVED:
4442 case DIFFSTATE_THEIRSADDED:
4443 case DIFFSTATE_YOURSREMOVED:
4444 case DIFFSTATE_YOURSADDED:
4445 case DIFFSTATE_EDITED:
4447 if (!m_bMatchCase)
4448 line = line.MakeLower();
4449 s = 0;
4450 e = 0;
4451 int match = 0;
4452 while (StringFound(line, SearchNext, s, e))
4454 match++;
4455 s = e;
4456 e = 0;
4458 m_arFindStringLines.push_back(match);
4459 break;
4461 default:
4462 m_arFindStringLines.push_back(0);
4465 else
4466 m_arFindStringLines.push_back(0);
4468 else
4469 m_arFindStringLines.push_back(0);
4471 UpdateLocator();
4474 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4476 if (!m_bShowInlineDiff)
4477 return false;
4478 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4479 return false;
4481 CString sLine = GetViewLineChars(nViewLine);
4482 if (sLine.IsEmpty())
4483 return false;
4485 CheckOtherView();
4486 if (!m_pOtherViewData)
4487 return false;
4489 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4490 if (sDiffLine.IsEmpty())
4491 return false;
4493 CString sLineExp = ExpandChars(sLine);
4494 CString sDiffLineExp = ExpandChars(sDiffLine);
4495 svn_diff_t * diff = NULL;
4496 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4497 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4498 return false;
4500 size_t lineoffset = 0;
4501 size_t position = 0;
4502 while (diff)
4504 apr_off_t len = diff->original_length;
4505 size_t oldpos = position;
4507 for (apr_off_t i = 0; i < len; ++i)
4509 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4510 lineoffset++;
4513 if (diff->type == svn_diff__type_diff_modified)
4515 inlineDiffPos p;
4516 p.start = oldpos;
4517 p.end = position;
4518 positions.push_back(p);
4521 diff = diff->next;
4524 return !positions.empty();
4527 void CBaseView::OnNavigateNextinlinediff()
4529 int nX;
4530 if (GetNextInlineDiff(nX))
4532 POINT ptCaretViewPos = GetCaretViewPosition();
4533 ptCaretViewPos.x = nX;
4534 SetCaretAndGoalViewPosition(ptCaretViewPos);
4535 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4536 EnsureCaretVisible();
4540 void CBaseView::OnNavigatePrevinlinediff()
4542 int nX;
4543 if (GetPrevInlineDiff(nX))
4545 POINT ptCaretViewPos = GetCaretViewPosition();
4546 ptCaretViewPos.x = nX;
4547 SetCaretAndGoalViewPosition(ptCaretViewPos);
4548 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4549 EnsureCaretVisible();
4553 bool CBaseView::HasNextInlineDiff()
4555 int nPos;
4556 return GetNextInlineDiff(nPos);
4559 bool CBaseView::GetNextInlineDiff(int & nPos)
4561 POINT ptCaretViewPos = GetCaretViewPosition();
4562 std::vector<inlineDiffPos> positions;
4563 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4565 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4567 if (it->start > ptCaretViewPos.x)
4569 nPos = (LONG)it->start;
4570 return true;
4572 if (it->end > ptCaretViewPos.x)
4574 nPos = (LONG)it->end;
4575 return true;
4579 return false;
4582 bool CBaseView::HasPrevInlineDiff()
4584 int nPos;
4585 return GetPrevInlineDiff(nPos);
4588 bool CBaseView::GetPrevInlineDiff(int & nPos)
4590 POINT ptCaretViewPos = GetCaretViewPosition();
4591 std::vector<inlineDiffPos> positions;
4592 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4594 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4596 if ( it->end < ptCaretViewPos.x)
4598 nPos = (LONG)it->end;
4599 return true;
4601 if ( it->start < ptCaretViewPos.x)
4603 nPos = (LONG)it->start;
4604 return true;
4608 return false;
4611 CBaseView * CBaseView::GetFirstGoodView()
4613 if (IsViewGood(m_pwndLeft))
4614 return m_pwndLeft;
4615 if (IsViewGood(m_pwndRight))
4616 return m_pwndRight;
4617 if (IsViewGood(m_pwndBottom))
4618 return m_pwndBottom;
4619 return NULL;
4622 void CBaseView::BuildAllScreen2ViewVector()
4624 CBaseView * p_pwndView = GetFirstGoodView();
4625 if (p_pwndView)
4627 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4631 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4633 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4636 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4638 CBaseView * p_pwndView = GetFirstGoodView();
4639 if (p_pwndView)
4641 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4645 void CBaseView::UpdateViewLineNumbers()
4647 int nLineNumber = 0;
4648 int nViewLineCount = GetViewCount();
4649 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4651 int oldLine = (int)GetViewLineNumber(nViewLine);
4652 if (oldLine >= 0)
4653 SetViewLineNumber(nViewLine, nLineNumber++);
4655 m_nDigits = 0;
4658 int CBaseView::CleanEmptyLines()
4660 int nRemovedCount = 0;
4661 int nViewLineCount = GetViewCount();
4662 bool bCheckLeft = IsViewGood(m_pwndLeft);
4663 bool bCheckRight = IsViewGood(m_pwndRight);
4664 bool bCheckBottom = IsViewGood(m_pwndBottom);
4665 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4667 bool bAllEmpty = true;
4668 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4669 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4670 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4671 if (bAllEmpty)
4673 if (bCheckLeft)
4675 m_pwndLeft->RemoveViewData(nViewLine);
4677 if (bCheckRight)
4679 m_pwndRight->RemoveViewData(nViewLine);
4681 if (bCheckBottom)
4683 m_pwndBottom->RemoveViewData(nViewLine);
4685 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4687 SaveUndoStep();
4689 nViewLineCount--;
4690 nRemovedCount++;
4691 continue;
4693 nViewLine++;
4695 return nRemovedCount;
4698 int CBaseView::FindScreenLineForViewLine( int viewLine )
4700 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4703 int CBaseView::CountMultiLines( int nViewLine )
4705 if (m_ScreenedViewLine.empty())
4706 return 0; // in case the view is completely empty
4708 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4710 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4712 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4715 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4717 TScreenedViewLine oScreenedLine;
4718 // tokenize string
4719 int prevpos = 0;
4720 int pos = 0;
4721 while ((pos = multiline.Find('\n', pos)) >= 0)
4723 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4724 pos++;
4725 prevpos = pos;
4727 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4728 oScreenedLine.bSublinesSet = true;
4729 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4731 return CountMultiLines(nViewLine);
4734 /// prepare inline diff cache
4735 LineColors & CBaseView::GetLineColors(int nViewLine)
4737 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4739 if (m_bWhitespaceInlineDiffs)
4741 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4742 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4744 else
4746 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4747 return m_ScreenedViewLine[nViewLine].lineColors;
4750 LineColors oLineColors;
4751 // set main line color
4752 COLORREF crBkgnd, crText;
4753 DiffStates diffState = m_pViewData->GetState(nViewLine);
4754 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4755 oLineColors.SetColor(0, crText, crBkgnd);
4757 do {
4758 if (!m_bShowInlineDiff)
4759 break;
4761 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4762 break;
4764 CString sLine = GetViewLineChars(nViewLine);
4765 if (sLine.IsEmpty())
4766 break;
4767 if (!m_pOtherView)
4768 break;
4770 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4771 if (sDiffLine.IsEmpty())
4772 break;
4774 svn_diff_t * diff = NULL;
4775 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4776 break;
4777 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4778 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4779 break;
4781 int lineoffset = 0;
4782 int nTextStartOffset = 0;
4783 std::map<int, COLORREF> removedPositions;
4784 while (diff)
4786 apr_off_t len = diff->original_length;
4788 CString s;
4789 for (int i = 0; i < len; ++i)
4791 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4792 lineoffset++;
4794 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4795 int nTextLength = s.GetLength();
4797 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4798 if ((m_bShowInlineDiff)&&(bInlineDiff))
4800 crBkgnd = InlineViewLineDiffColor(nViewLine);
4802 else
4804 crBkgnd = m_ModifiedBk;
4807 if (len < diff->modified_length)
4809 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4811 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4813 nTextStartOffset += nTextLength;
4814 diff = diff->next;
4816 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4818 oLineColors.AddShotColor(it->first, it->second);
4820 } while (false); // error catch
4822 if (!m_bWhitespaceInlineDiffs)
4824 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4825 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4827 else
4829 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4830 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4833 return GetLineColors(nViewLine);
4836 void CBaseView::OnEditSelectall()
4838 if (m_pViewData == nullptr)
4839 return;
4840 int nLastViewLine = m_pViewData->GetCount()-1;
4841 if (nLastViewLine < 0)
4842 return;
4843 SetupAllViewSelection(0, nLastViewLine);
4845 CString sLine = GetViewLineChars(nLastViewLine);
4846 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4847 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4848 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4850 UpdateWindow();
4853 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4855 FilterWhitespaces(first);
4856 FilterWhitespaces(second);
4859 void CBaseView::FilterWhitespaces(CString& line)
4861 line.Remove(' ');
4862 line.Remove('\t');
4863 line.Remove('\r');
4864 line.Remove('\n');
4867 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4869 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4870 int nEventLine = nLineFromTop + m_nTopLine;
4871 nEventLine--; //we need the index
4872 return nEventLine;
4876 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4878 if (RelayTrippleClick(pMsg))
4879 return TRUE;
4880 return CView::PreTranslateMessage(pMsg);
4884 void CBaseView::ResetUndoStep()
4886 m_AllState.Clear();
4889 void CBaseView::SaveUndoStep()
4891 if (!m_AllState.IsEmpty())
4893 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4895 ResetUndoStep();
4898 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4900 m_pState->addedlines.push_back(index);
4901 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4904 void CBaseView::InsertViewData( int index, const viewdata& data )
4906 m_pState->addedlines.push_back(index);
4907 m_pViewData->InsertData(index, data);
4910 void CBaseView::RemoveViewData( int index )
4912 m_pState->removedlines[index] = m_pViewData->GetData(index);
4913 m_pViewData->RemoveData(index);
4916 void CBaseView::SetViewData( int index, const viewdata& data )
4918 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4919 m_pViewData->SetData(index, data);
4922 void CBaseView::SetViewState( int index, DiffStates state )
4924 m_pState->linestates[index] = m_pViewData->GetState(index);
4925 m_pViewData->SetState(index, state);
4928 void CBaseView::SetViewLine( int index, const CString& sLine )
4930 m_pState->difflines[index] = m_pViewData->GetLine(index);
4931 m_pViewData->SetLine(index, sLine);
4934 void CBaseView::SetViewLineNumber( int index, int linenumber )
4936 int oldLineNumber = m_pViewData->GetLineNumber(index);
4937 if (oldLineNumber != linenumber) {
4938 m_pState->linelines[index] = oldLineNumber;
4939 m_pViewData->SetLineNumber(index, linenumber);
4943 void CBaseView::SetViewLineEnding( int index, EOL ending )
4945 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4946 m_pViewData->SetLineEnding(index, ending);
4949 void CBaseView::SetViewMarked( int index, bool marked )
4951 m_pViewData->SetMarked(index, marked);
4955 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4957 if (HasSelection())
4959 start = m_nSelViewBlockStart;
4960 end = m_nSelViewBlockEnd;
4961 return true;
4963 return false;
4966 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4968 RebuildIfNecessary();
4969 if (size() <= screenLine)
4970 return 0;
4971 return m_Screen2View[screenLine].nViewLine;
4974 int CBaseView::Screen2View::size()
4976 RebuildIfNecessary();
4977 return (int)m_Screen2View.size();
4980 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4982 RebuildIfNecessary();
4983 if (size() <= screenLine)
4984 return 0;
4985 return m_Screen2View[screenLine].nViewSubLine;
4988 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4990 RebuildIfNecessary();
4991 return m_Screen2View[screenLine];
4995 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4997 void CBaseView::Screen2View::RebuildIfNecessary()
4999 if (!m_pViewData)
5000 return; // rebuild not necessary
5002 FixScreenedCacheSize(m_pwndLeft);
5003 FixScreenedCacheSize(m_pwndRight);
5004 FixScreenedCacheSize(m_pwndBottom);
5005 if (!m_bFull)
5007 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5009 ResetScreenedViewLineCache(m_pwndLeft, *it);
5010 ResetScreenedViewLineCache(m_pwndRight, *it);
5011 ResetScreenedViewLineCache(m_pwndBottom, *it);
5014 else
5016 ResetScreenedViewLineCache(m_pwndLeft);
5017 ResetScreenedViewLineCache(m_pwndRight);
5018 ResetScreenedViewLineCache(m_pwndBottom);
5020 m_RebuildRanges.clear();
5021 m_bFull = false;
5023 size_t OldSize = m_Screen2View.size();
5024 m_Screen2View.clear();
5025 m_Screen2View.reserve(OldSize); // guess same size
5026 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5028 if (m_pMainFrame->m_bCollapsed)
5030 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5031 ++i;
5032 if (!(i < m_pViewData->GetCount()))
5033 break;
5035 TScreenLineInfo oLineInfo;
5036 oLineInfo.nViewLine = i;
5037 oLineInfo.nViewSubLine = -1; // no wrap
5038 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5040 int nMaxLines = 0;
5041 if (IsLeftViewGood())
5042 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5043 if (IsRightViewGood())
5044 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5045 if (IsBottomViewGood())
5046 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5047 for (int l = 0; l < (nMaxLines-1); ++l)
5049 oLineInfo.nViewSubLine++;
5050 m_Screen2View.push_back(oLineInfo);
5052 oLineInfo.nViewSubLine++;
5054 m_Screen2View.push_back(oLineInfo);
5056 m_pViewData = NULL;
5058 if (IsLeftViewGood())
5059 m_pwndLeft->BuildMarkedWordArray();
5060 if (IsRightViewGood())
5061 m_pwndRight->BuildMarkedWordArray();
5062 if (IsBottomViewGood())
5063 m_pwndBottom->BuildMarkedWordArray();
5064 UpdateLocator();
5065 RecalcAllVertScrollBars();
5066 RecalcAllHorzScrollBars();
5069 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5071 RebuildIfNecessary();
5073 int nScreenLineCount = (int)m_Screen2View.size();
5075 int nPos = 0;
5076 if (nScreenLineCount>16)
5078 // for enough long data search for last screen
5079 // with viewline less than one we are looking for
5080 // use approximate method (based on) binary search using asymmetric start point
5081 // in form 2**n (determined as MSB of length) to go around division and rounding;
5082 // this effectively looks for bit values from MSB to LSB
5084 int nTestBit;
5085 //GetMostSignificantBitValue
5086 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5087 nTestBit = nScreenLineCount;
5088 nTestBit |= nTestBit>>1;
5089 nTestBit |= nTestBit>>2;
5090 nTestBit |= nTestBit>>4;
5091 nTestBit |= nTestBit>>8;
5092 nTestBit |= nTestBit>>16;
5093 nTestBit ^= (nTestBit>>1);
5095 while (nTestBit)
5097 int nTestPos = nPos | nTestBit;
5098 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5100 nPos = nTestPos;
5102 nTestBit >>= 1;
5105 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5107 nPos++;
5110 return nPos;
5113 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5114 m_bFull = true;
5116 m_pViewData = pViewData;
5119 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5121 if (m_bFull)
5122 return;
5124 m_pViewData = pViewData;
5126 TRebuildRange Range;
5127 Range.FirstViewLine=nFirstViewLine;
5128 Range.LastViewLine=nLastViewLine;
5129 m_RebuildRanges.push_back(Range);
5132 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5134 if (!IsViewGood(pwndView))
5136 return false;
5138 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5139 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5140 if (nOldSize == nViewCount)
5142 return false;
5144 pwndView->m_ScreenedViewLine.resize(nViewCount);
5145 return true;
5148 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5150 if (!IsViewGood(pwndView))
5152 return false;
5154 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5155 ResetScreenedViewLineCache(pwndView, Range);
5156 return true;
5159 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5161 if (!IsViewGood(pwndView))
5163 return false;
5165 if (Range.LastViewLine == -1)
5167 return false;
5169 ASSERT(Range.FirstViewLine >= 0);
5170 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5171 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5173 pwndView->m_ScreenedViewLine[i].Clear();
5175 return false;
5178 void CBaseView::WrapChanged()
5180 m_nMaxLineLength = -1;
5181 m_nOffsetChar = 0;
5184 void CBaseView::OnEditFind()
5186 if (m_pFindDialog)
5187 return;
5189 m_pFindDialog = new CFindDlg(this);
5190 m_pFindDialog->Create(this);
5192 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5193 m_pFindDialog->SetReadonly(m_bReadonly);
5196 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5198 ASSERT(m_pFindDialog != NULL);
5200 if (m_pFindDialog->IsTerminating())
5202 // invalidate the handle identifying the dialog box.
5203 m_pFindDialog = NULL;
5204 return 0;
5207 if(m_pFindDialog->FindNext())
5209 //read data from dialog
5210 m_sFindText = m_pFindDialog->GetFindString();
5211 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5212 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5213 m_bWholeWord = m_pFindDialog->WholeWord();
5215 if (!m_bMatchCase)
5216 m_sFindText = m_sFindText.MakeLower();
5218 BuildFindStringArray();
5219 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5221 if (m_pFindDialog->SearchUp())
5222 OnEditFindprev();
5223 else
5224 OnEditFindnext();
5226 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5228 size_t count = 0;
5229 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5230 count += m_arFindStringLines[i];
5231 CString format;
5232 format.LoadString(IDS_FIND_COUNT);
5233 CString matches;
5234 matches.Format(format, count);
5235 m_pFindDialog->SetStatusText(matches);
5237 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5239 if (!IsWritable())
5240 return 0;
5241 bool bFound = false;
5242 if (m_pFindDialog->SearchUp())
5243 bFound = Search(SearchPrevious, true, true, false);
5244 else
5245 bFound = Search(SearchNext, true, true, false);
5246 if (bFound)
5248 CString sReplaceText = m_pFindDialog->GetReplaceString();
5249 CUndo::GetInstance().BeginGrouping();
5250 RemoveSelectedText();
5251 InsertText(sReplaceText);
5252 CUndo::GetInstance().EndGrouping();
5256 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5258 if (!IsWritable())
5259 return 0;
5260 bool bFound = false;
5261 int replaceCount = 0;
5262 POINT lastPoint = m_ptSelectionViewPosStart;
5263 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5264 CUndo::GetInstance().BeginGrouping();
5267 bFound = Search(SearchNext, true, false, true);
5268 if (bFound)
5270 CString sReplaceText = m_pFindDialog->GetReplaceString();
5271 RemoveSelectedText();
5272 InsertText(sReplaceText);
5273 ++replaceCount;
5275 } while (bFound);
5276 CUndo::GetInstance().EndGrouping();
5277 if (replaceCount == 0)
5278 m_ptSelectionViewPosStart = lastPoint;
5279 CString message;
5280 message.Format(IDS_FIND_REPLACED, replaceCount);
5281 if (m_pFindDialog)
5282 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5287 return 0;
5290 void CBaseView::OnEditFindnextStart()
5292 if (m_pViewData == nullptr)
5293 return;
5294 if (HasTextSelection())
5296 m_sFindText = GetSelectedText();
5297 m_bMatchCase = false;
5298 m_bLimitToDiff = false;
5299 m_bWholeWord = false;
5300 m_sFindText = m_sFindText.MakeLower();
5302 BuildFindStringArray();
5303 OnEditFindnext();
5305 else
5307 m_sFindText.Empty();
5308 BuildFindStringArray();
5312 void CBaseView::OnEditFindprevStart()
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 OnEditFindprev();
5327 else
5329 m_sFindText.Empty();
5330 BuildFindStringArray();
5334 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5336 if (srchDir == SearchPrevious)
5338 int laststart = -1;
5339 int laststart2 = -1;
5342 laststart2 = laststart;
5343 laststart = str.Find(m_sFindText, laststart + 1);
5344 } while (laststart >= 0 && laststart < start);
5345 start = laststart2;
5347 else
5348 start = str.Find(m_sFindText, start);
5349 end = start + m_sFindText.GetLength();
5350 bool bStringFound = (start >= 0);
5351 if (bStringFound && m_bWholeWord)
5353 if (start)
5354 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5356 if (bStringFound)
5358 if (str.GetLength() > end)
5359 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5362 return bStringFound;
5365 void CBaseView::OnEditFindprev()
5367 Search(SearchPrevious, false, true, false);
5370 void CBaseView::OnEditFindnext()
5372 Search(SearchNext, false, true, false);
5375 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5377 if (m_sFindText.IsEmpty())
5378 return false;
5379 if(!m_pViewData)
5380 return false;
5382 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5383 POINT end;
5384 end.y = m_pViewData->GetCount()-1;
5385 if (end.y < 0)
5386 return false;
5388 if (srchDir==SearchNext)
5389 end.x = GetViewLineLength(end.y);
5390 else
5392 end.x = m_ptSelectionViewPosStart.x;
5393 start.x = 0;
5396 if (!HasTextSelection())
5398 start.y = m_ptCaretViewPos.y;
5399 if (srchDir==SearchNext)
5400 start.x = m_ptCaretViewPos.x;
5401 else
5403 start.x = 0;
5404 end.x = m_ptCaretViewPos.x;
5407 CString sSelectedText;
5408 int startline = -1;
5409 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5411 if (nViewLine < 0)
5413 if (stopEof)
5414 return false;
5415 nViewLine = m_pViewData->GetCount()-1;
5416 startline = start.y;
5417 if (flashIfNotFound)
5419 if (m_pFindDialog)
5420 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5421 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5424 if (nViewLine > end.y)
5426 if (stopEof)
5427 return false;
5428 nViewLine = 0;
5429 startline = start.y;
5430 if (flashIfNotFound)
5432 if (m_pFindDialog)
5433 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5434 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5437 switch (m_pViewData->GetState(nViewLine))
5439 case DIFFSTATE_EMPTY:
5440 break;
5441 case DIFFSTATE_UNKNOWN:
5442 case DIFFSTATE_NORMAL:
5443 if (m_bLimitToDiff)
5444 break;
5445 case DIFFSTATE_REMOVED:
5446 case DIFFSTATE_REMOVEDWHITESPACE:
5447 case DIFFSTATE_ADDED:
5448 case DIFFSTATE_ADDEDWHITESPACE:
5449 case DIFFSTATE_WHITESPACE:
5450 case DIFFSTATE_WHITESPACE_DIFF:
5451 case DIFFSTATE_CONFLICTED:
5452 case DIFFSTATE_CONFLICTED_IGNORED:
5453 case DIFFSTATE_CONFLICTADDED:
5454 case DIFFSTATE_CONFLICTEMPTY:
5455 case DIFFSTATE_CONFLICTRESOLVED:
5456 case DIFFSTATE_IDENTICALREMOVED:
5457 case DIFFSTATE_IDENTICALADDED:
5458 case DIFFSTATE_THEIRSREMOVED:
5459 case DIFFSTATE_THEIRSADDED:
5460 case DIFFSTATE_YOURSREMOVED:
5461 case DIFFSTATE_YOURSADDED:
5462 case DIFFSTATE_EDITED:
5464 sSelectedText = GetViewLineChars(nViewLine);
5465 if (nViewLine == start.y && startline < 0)
5466 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5467 if (!m_bMatchCase)
5468 sSelectedText = sSelectedText.MakeLower();
5469 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5470 int endfound = 0;
5471 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5473 HighlightViewLines(nViewLine, nViewLine);
5474 m_ptSelectionViewPosStart.x = startfound;
5475 m_ptSelectionViewPosEnd.x = endfound;
5476 if (nViewLine == start.y && startline < 0)
5478 m_ptSelectionViewPosStart.x += start.x;
5479 m_ptSelectionViewPosEnd.x += start.x;
5481 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5482 m_ptSelectionViewPosStart.y = nViewLine;
5483 m_ptSelectionViewPosEnd.y = nViewLine;
5484 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5485 UpdateViewsCaretPosition();
5486 EnsureCaretVisible();
5487 Invalidate();
5488 return true;
5491 break;
5494 if (startline >= 0)
5496 if (nViewLine == startline)
5498 if (flashIfNotFound)
5500 CString message;
5501 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5502 if (m_pFindDialog)
5503 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5504 ::MessageBeep(0xFFFFFFFF);
5505 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5507 break;
5511 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5512 return false;
5515 CString CBaseView::GetSelectedText() const
5517 CString sSelectedText;
5518 POINT start = m_ptSelectionViewPosStart;
5519 POINT end = m_ptSelectionViewPosEnd;
5520 if (!HasTextSelection())
5522 if (!HasSelection())
5523 return sSelectedText;
5524 start.y = m_nSelViewBlockStart;
5525 start.x = 0;
5526 end.y = m_nSelViewBlockEnd;
5527 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5529 if (m_pViewData == nullptr)
5530 return sSelectedText;
5531 // first store the selected lines in one CString
5532 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5534 switch (m_pViewData->GetState(nViewLine))
5536 case DIFFSTATE_EMPTY:
5537 break;
5538 case DIFFSTATE_UNKNOWN:
5539 case DIFFSTATE_NORMAL:
5540 case DIFFSTATE_REMOVED:
5541 case DIFFSTATE_REMOVEDWHITESPACE:
5542 case DIFFSTATE_ADDED:
5543 case DIFFSTATE_ADDEDWHITESPACE:
5544 case DIFFSTATE_WHITESPACE:
5545 case DIFFSTATE_WHITESPACE_DIFF:
5546 case DIFFSTATE_CONFLICTED:
5547 case DIFFSTATE_CONFLICTED_IGNORED:
5548 case DIFFSTATE_CONFLICTADDED:
5549 case DIFFSTATE_CONFLICTEMPTY:
5550 case DIFFSTATE_CONFLICTRESOLVED:
5551 case DIFFSTATE_IDENTICALREMOVED:
5552 case DIFFSTATE_IDENTICALADDED:
5553 case DIFFSTATE_THEIRSREMOVED:
5554 case DIFFSTATE_THEIRSADDED:
5555 case DIFFSTATE_YOURSREMOVED:
5556 case DIFFSTATE_YOURSADDED:
5557 case DIFFSTATE_EDITED:
5558 sSelectedText += GetViewLineChars(nViewLine);
5559 sSelectedText += _T("\r\n");
5560 break;
5563 // remove the non-selected chars from the first line, last line and last \r\n
5564 int nLeftCut = start.x;
5565 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5566 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5567 return sSelectedText;
5570 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5572 hasMods = false;
5573 hasConflicts = false;
5574 hasWhitespaceMods = false;
5576 if (m_pViewData)
5578 for (int i=0; i<m_pViewData->GetCount(); i++)
5580 DiffStates state = m_pViewData->GetState(i);
5581 switch (state)
5583 case DIFFSTATE_ADDED:
5584 case DIFFSTATE_IDENTICALADDED:
5585 case DIFFSTATE_THEIRSADDED:
5586 case DIFFSTATE_YOURSADDED:
5587 case DIFFSTATE_CONFLICTADDED:
5588 case DIFFSTATE_IDENTICALREMOVED:
5589 case DIFFSTATE_REMOVED:
5590 case DIFFSTATE_THEIRSREMOVED:
5591 case DIFFSTATE_YOURSREMOVED:
5592 case DIFFSTATE_EMPTY:
5593 hasMods = true;
5594 break;
5595 case DIFFSTATE_CONFLICTED:
5596 case DIFFSTATE_CONFLICTED_IGNORED:
5597 hasConflicts = true;
5598 break;
5599 case DIFFSTATE_REMOVEDWHITESPACE:
5600 case DIFFSTATE_ADDEDWHITESPACE:
5601 case DIFFSTATE_WHITESPACE:
5602 case DIFFSTATE_WHITESPACE_DIFF:
5603 hasWhitespaceMods = true;
5604 break;
5610 void CBaseView::OnEditGotoline()
5612 if (m_pViewData == NULL)
5613 return;
5614 // find the last and first line number
5615 int nViewLineCount = m_pViewData->GetCount();
5617 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5618 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5620 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5621 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5623 break;
5626 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5628 return;
5630 nLastLineNumber++;
5631 int nFirstLineNumber=1; // first is always 1
5633 CString sText;
5634 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5636 CGotoLineDlg dlg(this);
5637 dlg.SetLabel(sText);
5638 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5639 if (dlg.DoModal() == IDOK)
5641 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5643 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5645 HighlightViewLines(nViewLine, nViewLine);
5646 return;
5652 void CBaseView::OnToggleReadonly()
5654 if (IsReadonlyChangable()) {
5655 SetWritable(IsReadonly());
5659 int CBaseView::SaveFile(int nFlags)
5661 Invalidate();
5662 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5664 CFileTextLines file;
5665 m_SaveParams.m_LineEndings = m_lineendings;
5666 m_SaveParams.m_UnicodeType = m_texttype;
5667 file.SetSaveParams(m_SaveParams);
5669 for (int i=0; i<m_pViewData->GetCount(); i++)
5671 //only copy non-removed lines
5672 DiffStates state = m_pViewData->GetState(i);
5673 switch (state)
5675 case DIFFSTATE_CONFLICTED:
5676 case DIFFSTATE_CONFLICTED_IGNORED:
5678 int first = i;
5679 int last = i;
5682 last++;
5683 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5684 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5685 for (int j=first; j<last; j++)
5687 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5689 file.Add(_T("======="), EOL_NOENDING);
5690 for (int j=first; j<last; j++)
5692 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5694 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5695 i = last-1;
5697 break;
5698 case DIFFSTATE_EMPTY:
5699 break;
5700 case DIFFSTATE_CONFLICTEMPTY:
5701 case DIFFSTATE_IDENTICALREMOVED:
5702 case DIFFSTATE_REMOVED:
5703 case DIFFSTATE_THEIRSREMOVED:
5704 case DIFFSTATE_YOURSREMOVED:
5705 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5706 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5708 // do not save removed lines
5709 break;
5711 default:
5712 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5713 break;
5716 CString filename = m_pWorkingFile->GetFilename();
5717 if (m_pWorkingFile->IsReadonly())
5718 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5719 return -1;
5720 if (!file.Save(filename))
5722 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5723 return -1;
5725 m_pWorkingFile->SetFileName(filename);
5726 m_pWorkingFile->StoreFileAttributes();
5727 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5728 SetModified(FALSE);
5729 CUndo::GetInstance().MarkAsOriginalState(
5730 this == m_pwndLeft,
5731 this == m_pwndRight,
5732 this == m_pwndBottom);
5733 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5734 return 0;
5735 return file.GetCount();
5737 return 1;
5741 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5743 if (m_pWorkingFile)
5745 m_pWorkingFile->SetFileName(sFileName);
5746 return SaveFile(nFlags);
5748 return -1;
5752 EOL CBaseView::GetLineEndings()
5754 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5757 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5759 if (bHasMixedEols)
5761 return EOL_AUTOLINE; // mixed eols - hack value
5763 if (m_lineendings == EOL_AUTOLINE)
5765 return EOL_CRLF;
5767 return m_lineendings;
5770 void CBaseView::ReplaceLineEndings(EOL eEol)
5772 if (eEol == EOL_AUTOLINE)
5774 return;
5776 // set AUTOLINE
5777 m_lineendings = eEol;
5778 // replace all set EOLs
5779 // TODO store line endings and lineendings in undo
5780 //CUndo::BeginGrouping();
5781 for (int i = 0; i < GetViewCount(); ++i)
5783 if (IsLineEmpty(i))
5785 continue;
5787 EOL eLineEol = GetViewLineEnding(i);
5788 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5790 continue;
5792 SetViewLineEnding(i, eEol);
5794 //CUndo::EndGrouping();
5795 //CUndo::saveundostep;
5796 DocumentUpdated();
5797 SetModified();
5800 void CBaseView::SetLineEndingStyle(EOL eEol)
5802 m_lineendings = eEol;
5805 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5807 if (m_texttype == eTextType)
5809 return;
5811 m_texttype = eTextType;
5812 DocumentUpdated();
5813 SetModified();
5816 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5818 if (IsReadonly())
5819 return; // nothing to be changed in read-only view
5820 CEncodingDlg dlg;
5821 dlg.view = CString(MAKEINTRESOURCE(nTextId));
5822 dlg.texttype = m_texttype;
5823 dlg.lineendings = GetLineEndings();
5824 if (dlg.DoModal() != IDOK)
5825 return;
5826 SetTextType(dlg.texttype);
5827 ReplaceLineEndings(dlg.lineendings);
5831 Replaces lines from source view to this
5833 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, bool skipMarked)
5835 if (!IsViewGood(pwndView))
5836 return;
5837 if (!IsWritable())
5838 return;
5839 CUndo::GetInstance().BeginGrouping();
5841 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5843 if (skipMarked && GetViewMarked(viewLine))
5844 continue;
5845 viewdata line = pwndView->GetViewData(viewLine);
5846 if (line.ending != EOL_NOENDING)
5847 line.ending = m_lineendings;
5848 switch (line.state)
5850 case DIFFSTATE_CONFLICTEMPTY:
5851 case DIFFSTATE_UNKNOWN:
5852 line.state = DIFFSTATE_EMPTY;
5853 case DIFFSTATE_EMPTY:
5854 break;
5855 case DIFFSTATE_ADDED:
5856 case DIFFSTATE_CONFLICTADDED:
5857 case DIFFSTATE_CONFLICTED:
5858 case DIFFSTATE_CONFLICTED_IGNORED:
5859 case DIFFSTATE_IDENTICALADDED:
5860 case DIFFSTATE_THEIRSADDED:
5861 case DIFFSTATE_YOURSADDED:
5862 case DIFFSTATE_IDENTICALREMOVED:
5863 case DIFFSTATE_REMOVED:
5864 case DIFFSTATE_THEIRSREMOVED:
5865 case DIFFSTATE_YOURSREMOVED:
5866 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5867 line.state = DIFFSTATE_NORMAL;
5868 case DIFFSTATE_NORMAL:
5869 break;
5870 default:
5871 break;
5873 SetViewData(viewLine, line);
5874 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5876 // if this view is in ASCII and the other is not, we have to make sure that
5877 // the text we copy from the other view can actually be saved in ASCII encoding.
5878 // if not, we have to change this views encoding to the same encoding as the other view
5879 BOOL useDefault = FALSE;
5880 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5881 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5882 SetTextType(pwndView->GetTextType());
5885 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5886 // TODO: check if copied line is same as original one set modified only when differ
5887 SetModified();
5888 SaveUndoStep();
5890 int nRemovedLines = CleanEmptyLines();
5891 SaveUndoStep();
5892 //VerifyEols();
5893 // make sure all non empty line have EOL set but last
5894 // wrong can be last copied line(have eol, but no line under),
5895 // or old last line (line before copied block missing eol, but have line under)
5896 // we'll check all lines to be sure
5897 int nLine = GetViewCount();
5898 // check last line have no EOL set
5899 while (--nLine>=0)
5901 if (!IsViewLineEmpty(nLine))
5903 if (GetViewLineEnding(nLine) != EOL_NOENDING)
5905 // we added non last line into empty block on the end (or should we remove eol from this one ?)
5906 // so next line is empty
5907 ASSERT(IsViewLineEmpty(nLine+1));
5908 // and we can turn it to normal empty line
5909 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
5911 break;
5914 // check all (nonlast) line have EOL set
5915 while (--nLine>=0)
5917 if (!IsViewLineEmpty(nLine))
5919 if (GetViewLineEnding(nLine) == EOL_NOENDING)
5921 SetViewLineEnding(nLine, m_lineendings);
5922 // in theory there should be only one line needing fix, but most of time we get over all anyway
5923 // break;
5927 SaveUndoStep();
5928 UpdateViewLineNumbers();
5929 SaveUndoStep();
5931 CUndo::GetInstance().EndGrouping();
5933 if (nRemovedLines!=0)
5935 // some lines are gone update selection
5936 ClearSelection();
5937 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
5939 BuildAllScreen2ViewVector();
5940 pwndView->Invalidate();
5941 RefreshViews();
5944 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
5946 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5947 SetViewMarked(viewLine, marked);
5948 BuildAllScreen2ViewVector();
5949 Invalidate();
5950 RefreshViews();
5953 void CBaseView::UseViewFileExceptMarked(CBaseView *pwndView)
5955 UseViewBlock(pwndView, 0, GetViewCount() - 1, true);
5958 int CBaseView::GetIndentCharsForLine(int x, int y)
5960 const int maxGuessLine = 100;
5961 int nTabMode = -1;
5962 CString line = GetViewLine(y);
5963 if (m_nTabMode & TABMODE_SMARTINDENT)
5965 // detect left char and right char
5966 TCHAR lc = x > 0 ? line[x - 1] : '\0';
5967 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
5968 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
5969 nTabMode = 1;
5970 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
5971 nTabMode = 0;
5972 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
5973 nTabMode = m_nTabMode & TABMODE_USESPACES;
5975 // detect lines nearby
5976 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
5978 bool above = i > 0 && i >= y - maxGuessLine;
5979 bool below = j < GetViewCount() && j <= y + maxGuessLine;
5980 if (!(above || below))
5981 break;
5982 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
5983 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
5984 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
5985 nTabMode = 1;
5986 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
5987 nTabMode = 0;
5988 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
5989 nTabMode = m_nTabMode & TABMODE_USESPACES;
5992 else
5993 nTabMode = m_nTabMode & TABMODE_USESPACES;
5995 if (nTabMode > 0)
5997 // use spaces
5998 x = CountExpandedChars(line, x);
5999 return (m_nTabSize - (x % m_nTabSize));
6002 // use tab
6003 return 0;
6006 void CBaseView::AddIndentationForSelectedBlock()
6008 bool bModified = false;
6009 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6011 // skip the line if no character is selected in the last selected line
6012 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6014 continue;
6016 // skip empty lines
6017 if (IsLineEmpty(nViewLine))
6019 continue;
6021 const CString &sLine = GetViewLine(nViewLine);
6022 CString sTemp = sLine;
6023 if (sTemp.Trim().IsEmpty())
6025 // skip empty and whitechar only lines
6026 continue;
6028 // add tab to line start (alternatively m_nTabSize spaces can be used)
6029 CString tabStr;
6030 int indentChars = GetIndentCharsForLine(0, nViewLine);
6031 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
6032 SetViewLine(nViewLine, tabStr + sLine);
6033 bModified = true;
6035 if (bModified)
6037 SetModified();
6038 SaveUndoStep();
6039 BuildAllScreen2ViewVector();
6043 void CBaseView::RemoveIndentationForSelectedBlock()
6045 bool bModified = false;
6046 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
6048 // skip the line if no character is selected in the last selected line
6049 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
6051 continue;
6053 // skip empty lines
6054 if (IsLineEmpty(nViewLine))
6056 continue;
6058 CString sLine = GetViewLine(nViewLine);
6059 // remove up to n spaces from line start
6060 // and one tab (if less then n spaces was removed)
6061 int nPos = 0;
6062 while (nPos<m_nTabSize)
6064 switch (sLine[nPos])
6066 case ' ':
6067 nPos++;
6068 continue;
6069 case '\t':
6070 nPos++;
6072 break;
6074 if (nPos>0)
6076 sLine.Delete(0, nPos);
6077 SetViewLine(nViewLine, sLine);
6078 bModified = true;
6081 if (bModified)
6083 SetModified();
6084 SaveUndoStep();
6085 BuildAllScreen2ViewVector();
6090 there are two possible versions
6091 - convert tabs to spaces only in front of text (implemented)
6092 - convert all tabs to spaces
6094 void CBaseView::ConvertTabToSpaces()
6096 bool bModified = false;
6097 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6099 if (IsLineEmpty(nViewLine))
6101 continue;
6103 const CString &sLine = GetViewLine(nViewLine);
6104 bool bTabToConvertFound = false;
6105 int nPosIn = 0;
6106 int nPosOut = 0;
6107 while (nPosIn<sLine.GetLength())
6109 switch (sLine[nPosIn])
6111 case ' ':
6112 nPosIn++;
6113 nPosOut++;
6114 continue;
6115 case '\t':
6116 nPosIn++;
6117 bTabToConvertFound = true;
6118 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6119 continue;
6121 break;
6123 if (bTabToConvertFound)
6125 CString sLineNew = sLine;
6126 sLineNew.Delete(0, nPosIn);
6127 sLineNew = CString(' ', nPosOut) + sLineNew;
6128 SetViewLine(nViewLine, sLineNew);
6129 bModified = true;
6132 if (bModified)
6134 SetModified();
6135 SaveUndoStep();
6136 BuildAllScreen2ViewVector();
6141 there are two possible version
6142 - convert spaces to tabs only in front of text (implemented)
6143 - convert all spaces to tabs
6145 void CBaseView::Tabularize()
6147 bool bModified = false;
6148 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6150 if (IsLineEmpty(nViewLine))
6152 continue;
6154 const CString &sLine = GetViewLine(nViewLine);
6155 int nDel = 0;
6156 int nTabCount = 0; // total tabs to be used
6157 int nSpaceCount = 0; // number of spaces in tab size run
6158 int nPos = 0;
6159 while (nPos<sLine.GetLength())
6161 switch (sLine[nPos++])
6163 case ' ':
6164 //bSpace = true;
6165 if (++nSpaceCount < m_nTabSize)
6167 continue;
6169 case '\t':
6170 nTabCount++;
6171 nSpaceCount = 0;
6172 nDel = nPos;
6173 continue;
6175 break;
6177 if (nDel > 0)
6179 CString sLineNew = sLine;
6180 sLineNew.Delete(0, nDel);
6181 sLineNew = CString('\t', nTabCount) + sLineNew;
6182 if (sLine!=sLineNew)
6184 SetViewLine(nViewLine, sLineNew);
6185 bModified = true;
6189 if (bModified)
6191 SetModified();
6192 SaveUndoStep();
6193 BuildAllScreen2ViewVector();
6197 void CBaseView::RemoveTrailWhiteChars()
6199 bool bModified = false;
6200 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6202 if (IsLineEmpty(nViewLine))
6204 continue;
6206 const CString &sLine = GetViewLine(nViewLine);
6207 CString sLineNew = sLine;
6208 sLineNew.TrimRight();
6209 if (sLine.GetLength()!=sLineNew.GetLength())
6211 SetViewLine(nViewLine, sLineNew);
6212 bModified = true;
6215 if (bModified)
6217 SetModified();
6218 SaveUndoStep();
6219 BuildAllScreen2ViewVector();
6223 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6225 if (GetViewCount()>10000)
6227 // 10k lines is enough to check
6228 TWhitecharsProperties oRet = {true, true, true, true};
6229 return oRet;
6231 TWhitecharsProperties oRet = {};
6232 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6234 if (IsLineEmpty(nViewLine))
6236 continue;
6238 const CString &sLine = GetViewLine(nViewLine);
6239 if (sLine.IsEmpty())
6241 continue;
6243 // check leading whites for convertible tabs and spaces
6244 int nPos = 0;
6245 int nSpaceCount = 0; // number of spaces in tab size run
6246 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6248 switch (sLine[nPos++])
6250 case ' ':
6251 if (++nSpaceCount >= m_nTabSize)
6253 oRet.HasSpacesToConvert = true;
6255 continue;
6256 case '\t':
6257 oRet.HasTabsToConvert = true;
6258 if (nSpaceCount!=0)
6260 oRet.HasSpacesToConvert = true;
6262 continue;
6264 break;
6267 // check trailing whites for removable chars
6268 switch (sLine[sLine.GetLength()-1])
6270 case ' ':
6271 case '\t':
6272 oRet.HasTrailWhiteChars = true;
6275 // check EOLs
6276 EOL eLineEol = GetViewLineEnding(nViewLine);
6277 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6279 oRet.HasMixedEols = true;
6282 return oRet;
6285 void CBaseView::InsertText(const CString& sText)
6287 ResetUndoStep();
6289 POINT ptCaretViewPos = GetCaretViewPosition();
6290 int nLeft = ptCaretViewPos.x;
6291 int nViewLine = ptCaretViewPos.y;
6293 if ((nViewLine == 0) && (GetViewCount() == 0))
6294 OnChar(VK_RETURN, 0, 0);
6296 std::vector<CString> lines;
6297 int nStart = 0;
6298 int nEolPos = 0;
6299 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6301 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6302 lines.push_back(sLine);
6303 nEolPos++;
6304 nStart = nEolPos;
6306 CString sLine = sText.Mid(nStart);
6307 lines.push_back(sLine);
6309 int nLinesToPaste = (int)lines.size();
6310 if (nLinesToPaste > 1)
6312 // multiline text
6314 // We want to undo the multiline insertion in a single step.
6315 CUndo::GetInstance().BeginGrouping();
6317 sLine = GetViewLineChars(nViewLine);
6318 CString sLineLeft = sLine.Left(nLeft);
6319 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6320 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6321 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6322 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6324 newLine.sLine = sLineLeft + lines[0];
6325 SetViewData(nViewLine, newLine);
6328 int nInsertLine = nViewLine;
6329 for (int i = 1; i < nLinesToPaste - 1; i++)
6331 newLine.sLine = lines[i];
6332 InsertViewData(++nInsertLine, newLine);
6334 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6335 newLine.ending = eOriginalEnding;
6336 InsertViewData(++nInsertLine, newLine);
6338 SetModified();
6339 SaveUndoStep();
6341 // adds new lines everywhere except me
6342 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6344 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6346 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6348 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6350 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6352 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6354 SaveUndoStep();
6356 UpdateViewLineNumbers();
6357 CUndo::GetInstance().EndGrouping();
6359 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6361 else
6363 // single line text - just insert it
6364 sLine = GetViewLineChars(nViewLine);
6365 sLine.Insert(nLeft, sText);
6366 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6367 SetViewLine(nViewLine, sLine);
6368 SetViewState(nViewLine, DIFFSTATE_EDITED);
6369 SetModified();
6370 SaveUndoStep();
6373 RefreshViews();
6374 BuildAllScreen2ViewVector();
6375 UpdateCaretViewPosition(ptCaretViewPos);