Refactored
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blobc95d33884384016dfa0e788fc02b6432edc95d0e
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)
108 m_ptCaretViewPos.x = 0;
109 m_ptCaretViewPos.y = 0;
110 m_ptSelectionViewPosStart = m_ptCaretViewPos;
111 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
112 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
113 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
114 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
115 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
116 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
117 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
118 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
119 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
120 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
121 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?"));
122 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
123 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
124 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
125 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
126 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
127 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
128 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
129 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
130 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
131 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
132 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
133 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
134 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
135 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
136 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
137 m_hMarkedIcon = LoadIcon(IDI_LINEMARKED);
138 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
140 for (int i=0; i<1024; ++i)
141 m_sConflictedText += _T("??");
142 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
144 m_szTip[0] = 0;
145 m_wszTip[0] = 0;
146 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
147 EnableToolTips();
149 m_Eols[EOL_LF] = L"\n"; // x0a
150 m_Eols[EOL_CR] = L"\r"; // x0d
151 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
152 m_Eols[EOL_LFCR] = L"\n\r";
153 m_Eols[EOL_VT] = L"\v"; // x0b
154 m_Eols[EOL_FF] = L"\f"; // x0c
155 m_Eols[EOL_NEL] = L"\x85";
156 m_Eols[EOL_LS] = L"\x2028";
157 m_Eols[EOL_PS] = L"\x2029";
158 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
159 ? EOL_CRLF
160 : m_lineendings];
161 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
162 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
165 CBaseView::~CBaseView()
167 ReleaseBitmap();
168 DeleteFonts();
169 DestroyIcon(m_hAddedIcon);
170 DestroyIcon(m_hRemovedIcon);
171 DestroyIcon(m_hConflictedIcon);
172 DestroyIcon(m_hConflictedIgnoredIcon);
173 DestroyIcon(m_hWhitespaceBlockIcon);
174 DestroyIcon(m_hEqualIcon);
175 DestroyIcon(m_hLineEndingCR);
176 DestroyIcon(m_hLineEndingCRLF);
177 DestroyIcon(m_hLineEndingLF);
178 DestroyIcon(m_hEditedIcon);
179 DestroyIcon(m_hMovedIcon);
180 DestroyIcon(m_hMarkedIcon);
181 DestroyCursor(m_margincursor);
184 BEGIN_MESSAGE_MAP(CBaseView, CView)
185 ON_WM_VSCROLL()
186 ON_WM_HSCROLL()
187 ON_WM_ERASEBKGND()
188 ON_WM_CREATE()
189 ON_WM_DESTROY()
190 ON_WM_SIZE()
191 ON_WM_MOUSEWHEEL()
192 ON_WM_MOUSEHWHEEL()
193 ON_WM_SETCURSOR()
194 ON_WM_KILLFOCUS()
195 ON_WM_SETFOCUS()
196 ON_WM_CONTEXTMENU()
197 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
198 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
199 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
200 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
201 ON_WM_KEYDOWN()
202 ON_WM_LBUTTONDOWN()
203 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
204 ON_WM_MOUSEMOVE()
205 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
206 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
207 ON_WM_CHAR()
208 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
209 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
210 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
211 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
212 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
213 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
214 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
215 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
216 ON_WM_TIMER()
217 ON_WM_LBUTTONDBLCLK()
218 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
219 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
220 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
221 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
222 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
223 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
224 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
225 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
226 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
227 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
228 ON_WM_LBUTTONUP()
229 END_MESSAGE_MAP()
232 void CBaseView::DocumentUpdated()
234 ReleaseBitmap();
235 m_nLineHeight = -1;
236 m_nCharWidth = -1;
237 m_nScreenChars = -1;
238 m_nLastScreenChars = -1;
239 m_nMaxLineLength = -1;
240 m_nScreenLines = -1;
241 m_nTopLine = 0;
242 m_bModified = FALSE;
243 m_bOtherDiffChecked = false;
244 m_nDigits = 0;
245 m_nMouseLine = -1;
246 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
247 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
248 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
249 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
250 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
251 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
252 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
253 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
254 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
255 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
256 ? EOL_CRLF
257 : m_lineendings];
258 DeleteFonts();
259 ClearCurrentSelection();
260 UpdateStatusBar();
261 Invalidate();
264 void CBaseView::UpdateStatusBar()
266 int nRemovedLines = 0;
267 int nAddedLines = 0;
268 int nConflictedLines = 0;
270 if (m_pViewData)
272 for (int i=0; i<m_pViewData->GetCount(); i++)
274 DiffStates state = m_pViewData->GetState(i);
275 switch (state)
277 case DIFFSTATE_ADDED:
278 case DIFFSTATE_IDENTICALADDED:
279 case DIFFSTATE_THEIRSADDED:
280 case DIFFSTATE_YOURSADDED:
281 case DIFFSTATE_CONFLICTADDED:
282 nAddedLines++;
283 break;
284 case DIFFSTATE_IDENTICALREMOVED:
285 case DIFFSTATE_REMOVED:
286 case DIFFSTATE_THEIRSREMOVED:
287 case DIFFSTATE_YOURSREMOVED:
288 nRemovedLines++;
289 break;
290 case DIFFSTATE_CONFLICTED:
291 case DIFFSTATE_CONFLICTED_IGNORED:
292 nConflictedLines++;
293 break;
298 CString sBarText;
299 CString sTemp;
301 if (m_pwndStatusBar)
303 sBarText += CFileTextLines::GetEncodingName(m_texttype);
304 sBarText += sBarText.IsEmpty() ? L"" : L" ";
305 sBarText += GetEolName(m_lineendings);
306 sBarText += sBarText.IsEmpty() ? L"" : L" ";
308 if (sBarText.IsEmpty())
309 sBarText += _T(" / ");
312 if (nRemovedLines)
314 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
315 if (!sBarText.IsEmpty())
316 sBarText += _T(" / ");
317 sBarText += sTemp;
319 if (nAddedLines)
321 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
322 if (!sBarText.IsEmpty())
323 sBarText += _T(" / ");
324 sBarText += sTemp;
326 if (nConflictedLines)
328 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
329 if (!sBarText.IsEmpty())
330 sBarText += _T(" / ");
331 sBarText += sTemp;
333 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
335 if (m_pwndStatusBar)
337 UINT nID;
338 UINT nStyle;
339 int cxWidth;
340 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
342 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
344 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
346 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
347 sBarText = sTemp+sBarText;
349 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
351 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
352 sBarText = sTemp+sBarText;
354 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
355 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
356 //calculate the width of the text
357 CDC * pDC = m_pwndStatusBar->GetDC();
358 if (pDC)
360 CSize size = pDC->GetTextExtent(sBarText);
361 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
362 ReleaseDC(pDC);
364 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
366 else if (m_pwndRibbonStatusBar)
368 if (!IsViewGood(m_pwndBottom))
369 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
370 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
372 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
373 std::unique_ptr<CMFCRibbonButtonsGroup> apBtnGroupBottom(new CMFCRibbonButtonsGroup);
374 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
375 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
376 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
377 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
378 apBtnGroupBottom->AddButton(pButton);
379 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
380 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
381 apBtnGroupBottom->AddButton(pButton);
382 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
383 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
386 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
387 if (pGroup)
389 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(3));
390 if (pPane)
392 pPane->SetText(sBarText);
394 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
395 if (pButton)
397 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
398 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
400 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
401 if (pButton)
403 pButton->SetText(GetEolName(m_lineendings));
404 pButton->SetDescription(GetEolName(m_lineendings));
407 m_pwndRibbonStatusBar->RecalcLayout();
408 m_pwndRibbonStatusBar->Invalidate();
413 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
415 if (!CView::PreCreateWindow(cs))
416 return FALSE;
418 cs.dwExStyle |= WS_EX_CLIENTEDGE;
419 cs.style &= ~WS_BORDER;
420 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
421 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
423 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
424 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
426 // View must always create its own scrollbars,
427 // if only it's not used within splitter
428 cs.style |= (WS_HSCROLL | WS_VSCROLL);
430 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
431 return TRUE;
434 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
436 int nIndex = 0;
437 if (bBold)
438 nIndex |= 1;
439 if (bItalic)
440 nIndex |= 2;
441 if (m_apFonts[nIndex] == NULL)
443 m_apFonts[nIndex] = new CFont;
444 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
445 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
446 m_lfBaseFont.lfItalic = (BYTE) bItalic;
447 CDC * pDC = GetDC();
448 if (pDC)
450 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
451 ReleaseDC(pDC);
453 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
454 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
456 delete m_apFonts[nIndex];
457 m_apFonts[nIndex] = NULL;
458 return CView::GetFont();
461 return m_apFonts[nIndex];
464 void CBaseView::CalcLineCharDim()
466 CDC *pDC = GetDC();
467 if (pDC == nullptr)
468 return;
469 CFont *pOldFont = pDC->SelectObject(GetFont());
470 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
471 pDC->SelectObject(pOldFont);
472 ReleaseDC(pDC);
474 m_nLineHeight = szCharExt.cy;
475 if (m_nLineHeight <= 0)
476 m_nLineHeight = -1;
477 m_nCharWidth = szCharExt.cx;
478 if (m_nCharWidth <= 0)
479 m_nCharWidth = -1;
482 int CBaseView::GetScreenChars()
484 if (m_nScreenChars == -1)
486 CRect rect;
487 GetClientRect(&rect);
488 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
489 if (m_nScreenChars < 0)
490 m_nScreenChars = 0;
492 return m_nScreenChars;
495 int CBaseView::GetAllMinScreenChars() const
497 int nChars = INT_MAX;
498 if (IsLeftViewGood())
499 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
500 if (IsRightViewGood())
501 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
502 if (IsBottomViewGood())
503 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
504 return (nChars==INT_MAX) ? 0 : nChars;
507 int CBaseView::GetAllMaxLineLength() const
509 int nLength = 0;
510 if (IsLeftViewGood())
511 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
512 if (IsRightViewGood())
513 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
514 if (IsBottomViewGood())
515 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
516 return nLength;
519 int CBaseView::GetLineHeight()
521 if (m_nLineHeight == -1)
522 CalcLineCharDim();
523 if (m_nLineHeight <= 0)
524 return 1;
525 return m_nLineHeight;
528 int CBaseView::GetCharWidth()
530 if (m_nCharWidth == -1)
531 CalcLineCharDim();
532 if (m_nCharWidth <= 0)
533 return 1;
534 return m_nCharWidth;
537 int CBaseView::GetMaxLineLength()
539 if (m_nMaxLineLength == -1)
541 m_nMaxLineLength = 0;
542 int nLineCount = GetLineCount();
543 for (int i=0; i<nLineCount; i++)
545 int nActualLength = GetLineLength(i);
546 if (m_nMaxLineLength < nActualLength)
547 m_nMaxLineLength = nActualLength;
550 return m_nMaxLineLength;
553 int CBaseView::GetLineLength(int index)
555 if (m_pViewData == NULL)
556 return 0;
557 if (m_pViewData->GetCount() == 0)
558 return 0;
559 if ((int)m_Screen2View.size() <= index)
560 return 0;
561 int viewLine = GetViewLineForScreen(index);
562 if (m_pMainFrame->m_bWrapLines)
564 int nLineLength = GetLineChars(index).GetLength();
565 ASSERT(nLineLength >= 0);
566 return nLineLength;
568 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
569 ASSERT(nLineLength >= 0);
570 return nLineLength;
573 int CBaseView::GetViewLineLength(int nViewLine) const
575 if (m_pViewData == NULL)
576 return 0;
577 if (m_pViewData->GetCount() <= nViewLine)
578 return 0;
579 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
580 ASSERT(nLineLength >= 0);
581 return nLineLength;
584 int CBaseView::GetLineCount() const
586 if (m_pViewData == NULL)
587 return 1;
588 int nLineCount = (int)m_Screen2View.size();
589 ASSERT(nLineCount >= 0);
590 return nLineCount;
593 int CBaseView::GetSubLineOffset(int index)
595 return m_Screen2View.GetSubLineOffset(index);
598 CString CBaseView::GetViewLineChars(int nViewLine) const
600 if (m_pViewData == NULL)
601 return 0;
602 if (m_pViewData->GetCount() <= nViewLine)
603 return 0;
604 return m_pViewData->GetLine(nViewLine);
607 CString CBaseView::GetLineChars(int index)
609 if (m_pViewData == NULL)
610 return 0;
611 if (m_pViewData->GetCount() == 0)
612 return 0;
613 if ((int)m_Screen2View.size() <= index)
614 return 0;
615 int viewLine = GetViewLineForScreen(index);
616 if (m_pMainFrame->m_bWrapLines)
618 int subLine = GetSubLineOffset(index);
619 if (subLine >= 0)
621 if (subLine < CountMultiLines(viewLine))
623 return m_ScreenedViewLine[viewLine].SubLines[subLine];
625 return L"";
628 return m_pViewData->GetLine(viewLine);
631 void CBaseView::CheckOtherView()
633 if (m_bOtherDiffChecked)
634 return;
635 // find out what the 'other' file is
636 m_pOtherViewData = NULL;
637 m_pOtherView = NULL;
638 if (this == m_pwndLeft && IsRightViewGood())
640 m_pOtherViewData = m_pwndRight->m_pViewData;
641 m_pOtherView = m_pwndRight;
644 if (this == m_pwndRight && IsLeftViewGood())
646 m_pOtherViewData = m_pwndLeft->m_pViewData;
647 m_pOtherView = m_pwndLeft;
650 m_bOtherDiffChecked = true;
654 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
656 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
657 ASSERT(viewData);
659 DiffStates origstate = viewData->GetState(nLineIndex);
661 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
662 nStartBlock = nLineIndex;
663 nEndBlock = nLineIndex;
664 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
666 DiffStates state = viewData->GetState(nStartBlock - 1);
667 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
668 origstate = state;
669 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
670 nStartBlock--;
671 else
672 break;
674 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
676 DiffStates state = viewData->GetState(nEndBlock + 1);
677 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
678 origstate = state;
679 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
680 nEndBlock++;
681 else
682 break;
686 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
688 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
690 int len = 0;
691 for (int i = nStartBlock; i <= nEndBlock; ++i)
692 len += viewData->GetLine(i).GetLength()+2;
694 CString block;
695 // do not check for whitespace blocks if the line is too long, because
696 // reserving a lot of memory here takes too much time (performance hog)
697 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
698 return block;
699 block.Preallocate(len+1);
700 for (int i = nStartBlock; i <= nEndBlock; ++i)
702 block += viewData->GetLine(i);
703 block += m_Eols[viewData->GetLineEnding(i)];
705 return block;
708 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
710 if (m_pViewData == NULL)
711 return false;
712 bIdentical = false;
713 CheckOtherView();
714 if (!m_pOtherViewData)
715 return false;
716 int viewLine = GetViewLineForScreen(nLineIndex);
717 if (
718 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
719 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
722 bIdentical = true;
723 return false;
725 // first check whether the line itself only has whitespace changes
726 CString mine = m_pViewData->GetLine(viewLine);
727 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
728 if (mine.IsEmpty() && other.IsEmpty())
730 bIdentical = true;
731 return false;
734 if (mine == other)
736 bIdentical = true;
737 return true;
739 FilterWhitespaces(mine, other);
740 if (mine == other)
741 return true;
743 int nStartBlock2, nEndBlock2;
744 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
745 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
746 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
747 if (mine.IsEmpty())
748 bIdentical = false;
749 else
751 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
752 bIdentical = mine == other;
753 FilterWhitespaces(mine, other);
756 return (!mine.IsEmpty()) && (mine == other);
759 bool CBaseView::IsViewLineHidden(int nViewLine)
761 return IsViewLineHidden(m_pViewData, nViewLine);
764 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
766 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
769 int CBaseView::GetLineNumber(int index) const
771 if (m_pViewData == NULL)
772 return -1;
773 int viewLine = GetViewLineForScreen(index);
774 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
775 return -1;
776 return m_pViewData->GetLineNumber(viewLine);
779 int CBaseView::GetScreenLines()
781 if (m_nScreenLines == -1)
783 SCROLLBARINFO sbi;
784 sbi.cbSize = sizeof(sbi);
785 int scrollBarHeight = 0;
786 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
787 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
788 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
789 scrollBarHeight = 0;
790 CRect rect;
791 GetClientRect(&rect);
792 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
793 if (m_nScreenLines < 0)
794 m_nScreenLines = 0;
796 return m_nScreenLines;
799 int CBaseView::GetAllMinScreenLines() const
801 int nLines = INT_MAX;
802 if (IsLeftViewGood())
803 nLines = m_pwndLeft->GetScreenLines();
804 if (IsRightViewGood())
805 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
806 if (IsBottomViewGood())
807 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
808 return (nLines==INT_MAX) ? 0 : nLines;
811 int CBaseView::GetAllLineCount() const
813 int nLines = 0;
814 if (IsLeftViewGood())
815 nLines = m_pwndLeft->GetLineCount();
816 if (IsRightViewGood())
817 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
818 if (IsBottomViewGood())
819 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
820 return nLines;
823 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
825 if (IsLeftViewGood())
826 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
827 if (IsRightViewGood())
828 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
829 if (IsBottomViewGood())
830 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
833 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
835 SCROLLINFO si;
836 si.cbSize = sizeof(si);
837 if (bPositionOnly)
839 si.fMask = SIF_POS;
840 si.nPos = m_nTopLine;
842 else
844 EnableScrollBarCtrl(SB_VERT, TRUE);
845 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
847 m_nTopLine = 0;
848 Invalidate();
850 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
851 si.nMin = 0;
852 si.nMax = GetAllLineCount();
853 si.nPage = GetAllMinScreenLines();
854 si.nPos = m_nTopLine;
856 VERIFY(SetScrollInfo(SB_VERT, &si));
859 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
861 CView::OnVScroll(nSBCode, nPos, pScrollBar);
862 if (m_pwndLeft)
863 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
864 if (m_pwndRight)
865 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
866 if (m_pwndBottom)
867 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
868 if (m_pwndLocator)
869 m_pwndLocator->Invalidate();
872 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
874 // Note we cannot use nPos because of its 16-bit nature
875 SCROLLINFO si;
876 si.cbSize = sizeof(si);
877 si.fMask = SIF_ALL;
878 VERIFY(master->GetScrollInfo(SB_VERT, &si));
880 int nPageLines = GetScreenLines();
881 int nLineCount = GetLineCount();
883 int nNewTopLine;
885 static LONG textwidth = 0;
886 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
887 switch (nSBCode)
889 case SB_TOP:
890 nNewTopLine = 0;
891 break;
892 case SB_BOTTOM:
893 nNewTopLine = nLineCount - nPageLines + 1;
894 break;
895 case SB_LINEUP:
896 nNewTopLine = m_nTopLine - 1;
897 break;
898 case SB_LINEDOWN:
899 nNewTopLine = m_nTopLine + 1;
900 break;
901 case SB_PAGEUP:
902 nNewTopLine = m_nTopLine - si.nPage + 1;
903 break;
904 case SB_PAGEDOWN:
905 nNewTopLine = m_nTopLine + si.nPage - 1;
906 break;
907 case SB_THUMBPOSITION:
908 m_ScrollTool.Clear();
909 nNewTopLine = si.nTrackPos;
910 textwidth = 0;
911 break;
912 case SB_THUMBTRACK:
913 nNewTopLine = si.nTrackPos;
914 if (GetFocus() == this)
916 RECT thumbrect;
917 GetClientRect(&thumbrect);
918 ClientToScreen(&thumbrect);
920 POINT thumbpoint;
921 thumbpoint.x = thumbrect.right;
922 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
923 m_ScrollTool.Init(&thumbpoint);
924 if (textwidth == 0)
926 CString sTemp = sFormat;
927 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
928 textwidth = m_ScrollTool.GetTextWidth(sTemp);
930 thumbpoint.x -= textwidth;
931 int line = GetLineNumber(nNewTopLine);
932 if (line >= 0)
933 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
934 else
935 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
937 break;
938 default:
939 return;
942 if (nNewTopLine < 0)
943 nNewTopLine = 0;
944 if (nNewTopLine >= nLineCount)
945 nNewTopLine = nLineCount - 1;
946 ScrollToLine(nNewTopLine);
949 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
951 if (IsLeftViewGood())
952 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
953 if (IsRightViewGood())
954 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
955 if (IsBottomViewGood())
956 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
959 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
961 SCROLLINFO si;
962 si.cbSize = sizeof(si);
963 if (bPositionOnly)
965 si.fMask = SIF_POS;
966 si.nPos = m_nOffsetChar;
968 else
970 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
971 if (!m_pMainFrame->m_bWrapLines)
973 int minScreenChars = GetAllMinScreenChars();
974 int maxLineLength = GetAllMaxLineLength();
975 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
977 m_nOffsetChar = 0;
978 Invalidate();
980 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
981 si.nMin = 0;
982 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
983 si.nMax += GetMarginWidth()/GetCharWidth();
984 si.nPage = minScreenChars;
985 si.nPos = m_nOffsetChar;
988 VERIFY(SetScrollInfo(SB_HORZ, &si));
991 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
993 CView::OnHScroll(nSBCode, nPos, pScrollBar);
994 if (m_pwndLeft)
995 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
996 if (m_pwndRight)
997 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
998 if (m_pwndBottom)
999 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1000 if (m_pwndLocator)
1001 m_pwndLocator->Invalidate();
1004 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1006 SCROLLINFO si;
1007 si.cbSize = sizeof(si);
1008 si.fMask = SIF_ALL;
1009 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1011 int nPageChars = GetScreenChars();
1012 int nMaxLineLength = GetMaxLineLength();
1014 int nNewOffset;
1015 switch (nSBCode)
1017 case SB_LEFT:
1018 nNewOffset = 0;
1019 break;
1020 case SB_BOTTOM:
1021 nNewOffset = nMaxLineLength - nPageChars + 1;
1022 break;
1023 case SB_LINEUP:
1024 nNewOffset = m_nOffsetChar - 1;
1025 break;
1026 case SB_LINEDOWN:
1027 nNewOffset = m_nOffsetChar + 1;
1028 break;
1029 case SB_PAGEUP:
1030 nNewOffset = m_nOffsetChar - si.nPage + 1;
1031 break;
1032 case SB_PAGEDOWN:
1033 nNewOffset = m_nOffsetChar + si.nPage - 1;
1034 break;
1035 case SB_THUMBPOSITION:
1036 case SB_THUMBTRACK:
1037 nNewOffset = si.nTrackPos;
1038 break;
1039 default:
1040 return;
1043 if (nNewOffset >= nMaxLineLength)
1044 nNewOffset = nMaxLineLength - 1;
1045 if (nNewOffset < 0)
1046 nNewOffset = 0;
1047 ScrollToChar(nNewOffset, TRUE);
1050 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1052 if (m_nOffsetChar != nNewOffsetChar)
1054 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1055 m_nOffsetChar = nNewOffsetChar;
1056 CRect rcScroll;
1057 GetClientRect(&rcScroll);
1058 rcScroll.left += GetMarginWidth();
1059 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1060 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1061 // update the view header
1062 rcScroll.left = 0;
1063 rcScroll.top = 0;
1064 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1065 InvalidateRect(&rcScroll, FALSE);
1066 UpdateWindow();
1067 if (bTrackScrollBar)
1068 RecalcHorzScrollBar(TRUE);
1069 UpdateCaret();
1070 if (m_pwndLineDiffBar)
1071 m_pwndLineDiffBar->Invalidate();
1075 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1077 if (m_pwndLeft)
1078 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1079 if (m_pwndRight)
1080 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1081 if (m_pwndBottom)
1082 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1085 void CBaseView::ScrollAllSide(int delta)
1087 int nNewOffset = m_nOffsetChar;
1088 nNewOffset += delta;
1089 int nMaxLineLength = GetMaxLineLength();
1090 if (nNewOffset >= nMaxLineLength)
1091 nNewOffset = nMaxLineLength - 1;
1092 if (nNewOffset < 0)
1093 nNewOffset = 0;
1094 ScrollAllToChar(nNewOffset, TRUE);
1095 if (m_pwndLineDiffBar)
1096 m_pwndLineDiffBar->Invalidate();
1097 UpdateCaret();
1100 void CBaseView::ScrollSide(int delta)
1102 int nNewOffset = m_nOffsetChar;
1103 nNewOffset += delta;
1104 int nMaxLineLength = GetMaxLineLength();
1105 if (nNewOffset >= nMaxLineLength)
1106 nNewOffset = nMaxLineLength - 1;
1107 if (nNewOffset < 0)
1108 nNewOffset = 0;
1109 ScrollToChar(nNewOffset, TRUE);
1110 if (m_pwndLineDiffBar)
1111 m_pwndLineDiffBar->Invalidate();
1112 UpdateCaret();
1115 void CBaseView::ScrollVertical(short zDelta)
1117 const int nLineCount = GetLineCount();
1118 int nTopLine = m_nTopLine;
1119 nTopLine -= (zDelta/30);
1120 if (nTopLine < 0)
1121 nTopLine = 0;
1122 if (nTopLine >= nLineCount)
1123 nTopLine = nLineCount - 1;
1124 ScrollToLine(nTopLine, TRUE);
1127 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1129 if (m_nTopLine != nNewTopLine)
1131 if (nNewTopLine < 0)
1132 nNewTopLine = 0;
1134 int nScrollLines = m_nTopLine - nNewTopLine;
1136 m_nTopLine = nNewTopLine;
1137 CRect rcScroll;
1138 GetClientRect(&rcScroll);
1139 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1140 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1141 UpdateWindow();
1142 if (bTrackScrollBar)
1143 RecalcVertScrollBar(TRUE);
1144 UpdateCaret();
1149 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1151 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1153 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1155 int nViewLine = GetViewLineForScreen(nLineIndex);
1156 HICON icon = NULL;
1157 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1158 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1159 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1161 DiffStates state = m_pViewData->GetState(nViewLine);
1162 switch (state)
1164 case DIFFSTATE_ADDED:
1165 case DIFFSTATE_THEIRSADDED:
1166 case DIFFSTATE_YOURSADDED:
1167 case DIFFSTATE_IDENTICALADDED:
1168 case DIFFSTATE_CONFLICTADDED:
1169 eIcon = TScreenedViewLine::ICN_ADD;
1170 break;
1171 case DIFFSTATE_REMOVED:
1172 case DIFFSTATE_THEIRSREMOVED:
1173 case DIFFSTATE_YOURSREMOVED:
1174 case DIFFSTATE_IDENTICALREMOVED:
1175 eIcon = TScreenedViewLine::ICN_REMOVED;
1176 break;
1177 case DIFFSTATE_CONFLICTED:
1178 eIcon = TScreenedViewLine::ICN_CONFLICT;
1179 break;
1180 case DIFFSTATE_CONFLICTED_IGNORED:
1181 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1182 break;
1183 case DIFFSTATE_EDITED:
1184 eIcon = TScreenedViewLine::ICN_EDIT;
1185 break;
1186 default:
1187 break;
1189 bool bIdentical = false;
1190 int blockstart = -1;
1191 int blockend = -1;
1192 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1194 if (bIdentical)
1195 eIcon = TScreenedViewLine::ICN_SAME;
1196 else
1197 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1198 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1200 if (nViewLine > blockstart)
1201 Invalidate(); // redraw the upper icons since they're now changing
1202 while (blockstart <= blockend)
1203 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1206 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1207 eIcon = TScreenedViewLine::ICN_MOVED;
1208 if (m_pViewData->GetMarked(nViewLine))
1209 eIcon = TScreenedViewLine::ICN_MARKED;
1210 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1212 switch (eIcon)
1214 case TScreenedViewLine::ICN_UNKNOWN:
1215 case TScreenedViewLine::ICN_NONE:
1216 break;
1217 case TScreenedViewLine::ICN_SAME:
1218 icon = m_hEqualIcon;
1219 break;
1220 case TScreenedViewLine::ICN_EDIT:
1221 icon = m_hEditedIcon;
1222 break;
1223 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1224 icon = m_hWhitespaceBlockIcon;
1225 break;
1226 case TScreenedViewLine::ICN_ADD:
1227 icon = m_hAddedIcon;
1228 break;
1229 case TScreenedViewLine::ICN_CONFLICT:
1230 icon = m_hConflictedIcon;
1231 break;
1232 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1233 icon = m_hConflictedIgnoredIcon;
1234 break;
1235 case TScreenedViewLine::ICN_REMOVED:
1236 icon = m_hRemovedIcon;
1237 break;
1238 case TScreenedViewLine::ICN_MOVED:
1239 icon = m_hMovedIcon;
1240 break;
1241 case TScreenedViewLine::ICN_MARKED:
1242 icon = m_hMarkedIcon;
1243 break;
1247 if (icon)
1249 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1251 if ((m_bViewLinenumbers)&&(m_nDigits))
1253 int nSubLine = GetSubLineOffset(nLineIndex);
1254 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1255 CString sLinenumber;
1256 if (bIsFirstSubline)
1258 CString sLinenumberFormat;
1259 int nLineNumber = GetLineNumber(nLineIndex);
1260 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1262 // TODO: do not show if there is no number hidden
1263 // TODO: show number if there is only one
1264 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1265 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1267 else if (nLineNumber >= 0)
1269 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1270 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1272 else if (m_pMainFrame->m_bWrapLines)
1274 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1275 sLinenumber.Format(sLinenumberFormat, _T("·"));
1277 if (!sLinenumber.IsEmpty())
1279 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1280 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1282 pdc->SelectObject(GetFont());
1283 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1290 int CBaseView::GetMarginWidth()
1292 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1294 if (m_nDigits <= 0)
1296 int nLength = (int)m_pViewData->GetCount();
1297 // find out how many digits are needed to show the highest line number
1298 CString sMax;
1299 sMax.Format(_T("%d"), nLength);
1300 m_nDigits = sMax.GetLength();
1302 int nWidth = GetCharWidth();
1303 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1305 return MARGINWIDTH;
1308 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1310 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1311 COLORREF crBk, crFg;
1312 if (IsBottomViewGood())
1314 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1315 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1317 else
1319 DiffStates state = DIFFSTATE_REMOVED;
1320 if (this == m_pwndRight)
1322 state = DIFFSTATE_ADDED;
1324 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1326 pdc->SetBkColor(crBk);
1327 pdc->FillSolidRect(textrect, crBk);
1329 pdc->SetTextColor(crFg);
1331 pdc->SelectObject(GetFont(FALSE, TRUE));
1333 CString sViewTitle;
1334 if (IsModified())
1336 sViewTitle = _T("* ") + m_sWindowName;
1338 else
1340 sViewTitle = m_sWindowName;
1342 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1343 if (nStringLength > rect.Width())
1345 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1346 sViewTitle = m_sWindowName.Mid(offset);
1348 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1349 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1350 if (this->GetFocus() == this)
1351 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1352 else
1353 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1356 void CBaseView::OnDraw(CDC * pDC)
1358 CRect rcClient;
1359 GetClientRect(rcClient);
1361 int nLineCount = GetLineCount();
1362 int nLineHeight = GetLineHeight();
1364 CDC cacheDC;
1365 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1366 if (m_pCacheBitmap == NULL)
1368 m_pCacheBitmap = new CBitmap;
1369 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1371 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1373 DrawHeader(pDC, rcClient);
1375 CRect rcLine;
1376 rcLine = rcClient;
1377 rcLine.top += nLineHeight+HEADERHEIGHT;
1378 rcLine.bottom = rcLine.top + nLineHeight;
1379 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1380 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1382 int nCurrentLine = m_nTopLine;
1383 bool bBeyondFileLineCached = false;
1384 while (rcLine.top < rcClient.bottom)
1386 if (nCurrentLine < nLineCount)
1388 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1389 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1390 bBeyondFileLineCached = false;
1392 else if (!bBeyondFileLineCached)
1394 DrawMargin(&cacheDC, rcCacheMargin, -1);
1395 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1396 bBeyondFileLineCached = true;
1399 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1401 nCurrentLine ++;
1402 rcLine.OffsetRect(0, nLineHeight);
1405 cacheDC.SelectObject(pOldBitmap);
1406 cacheDC.DeleteDC();
1409 bool CBaseView::IsStateConflicted(DiffStates state)
1411 switch (state)
1413 case DIFFSTATE_CONFLICTED:
1414 case DIFFSTATE_CONFLICTED_IGNORED:
1415 case DIFFSTATE_CONFLICTEMPTY:
1416 case DIFFSTATE_CONFLICTADDED:
1417 return true;
1419 return false;
1422 bool CBaseView::IsStateEmpty(DiffStates state)
1424 switch (state)
1426 case DIFFSTATE_CONFLICTEMPTY:
1427 case DIFFSTATE_UNKNOWN:
1428 case DIFFSTATE_EMPTY:
1429 return true;
1431 return false;
1434 bool CBaseView::IsStateRemoved(DiffStates state)
1436 switch (state)
1438 case DIFFSTATE_REMOVED:
1439 case DIFFSTATE_THEIRSREMOVED:
1440 case DIFFSTATE_YOURSREMOVED:
1441 case DIFFSTATE_IDENTICALREMOVED:
1442 return true;
1444 return false;
1447 DiffStates CBaseView::ResolveState(DiffStates state)
1449 if (IsStateConflicted(state))
1451 if (state == DIFFSTATE_CONFLICTEMPTY)
1452 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1453 else
1454 return DIFFSTATE_CONFLICTRESOLVED;
1456 return state;
1460 bool CBaseView::IsLineEmpty(int nLineIndex)
1462 if (m_pViewData == 0)
1463 return FALSE;
1464 int nViewLine = GetViewLineForScreen(nLineIndex);
1465 return IsViewLineEmpty(nViewLine);
1468 bool CBaseView::IsViewLineEmpty(int nViewLine)
1470 if (m_pViewData == 0)
1471 return FALSE;
1472 const DiffStates state = m_pViewData->GetState(nViewLine);
1473 return IsStateEmpty(state);
1476 bool CBaseView::IsLineRemoved(int nLineIndex)
1478 if (m_pViewData == 0)
1479 return FALSE;
1480 int nViewLine = GetViewLineForScreen(nLineIndex);
1481 return IsViewLineRemoved(nViewLine);
1484 bool CBaseView::IsViewLineRemoved(int nViewLine)
1486 if (m_pViewData == 0)
1487 return FALSE;
1488 const DiffStates state = m_pViewData->GetState(nViewLine);
1489 return IsStateRemoved(state);
1492 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1494 if (m_pViewData == 0)
1495 return false;
1496 const DiffStates state = m_pViewData->GetState(nLineIndex);
1497 return IsStateConflicted(state);
1500 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1502 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1505 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1507 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1510 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1512 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1513 return;
1514 int viewLine = GetViewLineForScreen(nLineIndex);
1515 EOL ending = m_pViewData->GetLineEnding(viewLine);
1516 if (m_bIconLFs)
1518 HICON hEndingIcon = NULL;
1519 switch (ending)
1521 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1522 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1523 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1524 default: return;
1526 if (origin.x < (rc.left-GetCharWidth()))
1527 return;
1528 // If EOL style has changed, color end-of-line markers as inline differences.
1530 m_bShowInlineDiff && m_pOtherViewData &&
1531 (viewLine < m_pOtherViewData->GetCount()) &&
1532 (ending != EOL_NOENDING) &&
1533 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1534 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1537 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1540 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1542 else
1544 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1545 CPen * oldpen = pDC->SelectObject(&pen);
1546 int yMiddle = origin.y + rc.Height()/2;
1547 int xMiddle = origin.x+GetCharWidth()/2;
1548 bool bMultiline = false;
1549 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1551 if (GetLineLength(nLineIndex+1))
1553 // multiline
1554 bMultiline = true;
1555 pDC->MoveTo(origin.x, yMiddle-2);
1556 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1557 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1558 pDC->LineTo(origin.x, yMiddle+2);
1560 else if (GetLineLength(nLineIndex) == 0)
1561 bMultiline = true;
1563 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1564 bMultiline = true;
1566 if (!bMultiline)
1568 switch (ending)
1570 case EOL_AUTOLINE:
1571 case EOL_CRLF:
1572 // arrow from top to middle+2, then left
1573 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1574 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1575 case EOL_CR:
1576 // arrow from right to left
1577 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1578 pDC->LineTo(origin.x, yMiddle);
1579 pDC->LineTo(origin.x+4, yMiddle+4);
1580 pDC->MoveTo(origin.x, yMiddle);
1581 pDC->LineTo(origin.x+4, yMiddle-4);
1582 break;
1583 case EOL_LFCR:
1584 // from right-upper to left then down
1585 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1586 pDC->LineTo(xMiddle, yMiddle-2);
1587 pDC->LineTo(xMiddle, rc.bottom-1);
1588 pDC->LineTo(xMiddle+4, rc.bottom-5);
1589 pDC->MoveTo(xMiddle, rc.bottom-1);
1590 pDC->LineTo(xMiddle-4, rc.bottom-5);
1591 break;
1592 case EOL_LF:
1593 // arrow from top to bottom
1594 pDC->MoveTo(xMiddle, rc.top);
1595 pDC->LineTo(xMiddle, rc.bottom-1);
1596 pDC->LineTo(xMiddle+4, rc.bottom-5);
1597 pDC->MoveTo(xMiddle, rc.bottom-1);
1598 pDC->LineTo(xMiddle-4, rc.bottom-5);
1599 break;
1600 case EOL_FF: // Form Feed, U+000C
1601 case EOL_NEL: // Next Line, U+0085
1602 case EOL_LS: // Line Separator, U+2028
1603 case EOL_PS: // Paragraph Separator, U+2029
1604 // draw a horizontal line at the bottom of this line
1605 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1606 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1607 pDC->LineTo(origin.x, rc.bottom-2);
1608 pDC->LineTo(origin.x+5, rc.bottom-2);
1609 pDC->MoveTo(origin.x, rc.bottom-2);
1610 pDC->LineTo(origin.x+1, rc.bottom-6);
1611 break;
1612 default: // other EOLs
1613 // arrow from top right to bottom left
1614 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1615 pDC->LineTo(origin.x, rc.bottom-1);
1616 pDC->LineTo(origin.x+5, rc.bottom-2);
1617 pDC->MoveTo(origin.x, rc.bottom-1);
1618 pDC->LineTo(origin.x+1, rc.bottom-6);
1619 break;
1620 case EOL_NOENDING:
1621 break;
1624 pDC->SelectObject(oldpen);
1628 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1630 if (!m_bShowSelection)
1631 return;
1633 int nSelBlockStart;
1634 int nSelBlockEnd;
1635 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1636 return;
1638 const int THICKNESS = 2;
1639 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1641 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1642 int nSubLine = GetSubLineOffset(nLineIndex);
1643 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1644 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1646 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1649 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1650 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1652 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1656 void CBaseView::DrawTextLine(
1657 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1659 ASSERT(nLineIndex < GetLineCount());
1660 int nViewLine = GetViewLineForScreen(nLineIndex);
1661 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1663 LineColors lineCols = GetLineColors(nViewLine);
1665 CString sViewLine = GetViewLineChars(nViewLine);
1666 // mark selection
1667 if (m_bShowSelection && HasTextSelection())
1669 // has this line selection ?
1670 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1672 int nViewLineLength = sViewLine.GetLength();
1674 // first suppose the whole line is selected
1675 int selectedStart = 0;
1676 int selectedEnd = nViewLineLength;
1678 // the view line is partially selected
1679 if (m_ptSelectionViewPosStart.y == nViewLine)
1681 selectedStart = m_ptSelectionViewPosStart.x;
1684 if (m_ptSelectionViewPosEnd.y == nViewLine)
1686 selectedEnd = m_ptSelectionViewPosEnd.x;
1688 // apply selection coloring
1689 // First enforce start and end point
1690 lineCols.SplitBlock(selectedStart);
1691 lineCols.SplitBlock(selectedEnd);
1692 // change color of affected parts
1693 long intenseColorScale = m_bFocused ? 70 : 30;
1694 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1695 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1697 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1698 if (it->second.shot == it->second.background)
1700 it->second.shot = crBk;
1702 it->second.background = crBk;
1703 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1708 // TODO: remove duplicate from selection and mark
1709 if (!m_sMarkedWord.IsEmpty())
1711 int nMarkLength = m_sMarkedWord.GetLength();
1712 //int nViewLineLength = sViewLine.GetLength();
1713 const TCHAR * text = sViewLine;
1714 const TCHAR * findText = text;
1715 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1717 int nMarkStart = static_cast<int>(findText - text);
1718 int nMarkEnd = nMarkStart + nMarkLength;
1719 findText += nMarkLength;
1720 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1721 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1722 if (eLeft == eStart)
1723 continue;
1724 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1725 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1726 if (eRight == eEnd)
1727 continue;
1729 // First enforce start and end point
1730 lineCols.SplitBlock(nMarkStart);
1731 lineCols.SplitBlock(nMarkEnd);
1732 // change color of affected parts
1733 const long int nIntenseColorScale = 200;
1734 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1735 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1737 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1738 if (it->second.shot == it->second.background)
1740 it->second.shot = crBk;
1742 it->second.background = crBk;
1743 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1747 if (!m_sFindText.IsEmpty())
1749 int nMarkStart = 0;
1750 int nMarkEnd = 0;
1751 int nStringPos = nMarkStart;
1752 CString searchLine = sViewLine;
1753 if (!m_bMatchCase)
1754 searchLine.MakeLower();
1755 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1757 // First enforce start and end point
1758 lineCols.SplitBlock(nMarkStart+nStringPos);
1759 lineCols.SplitBlock(nMarkEnd+nStringPos);
1760 // change color of affected parts
1761 const long int nIntenseColorScale = 30;
1762 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1763 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1765 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1766 if (it->second.shot == it->second.background)
1768 it->second.shot = crBk;
1770 it->second.background = crBk;
1771 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1773 searchLine = searchLine.Mid(nMarkEnd);
1774 nStringPos = nMarkEnd;
1775 nMarkStart = 0;
1776 nMarkEnd = 0;
1780 // @ this point we may cache data for next line which may be same in wrapped mode
1782 int nTextOffset = 0;
1783 int nSubline = GetSubLineOffset(nLineIndex);
1784 for (int n=0; n<nSubline; n++)
1786 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1787 nTextOffset += sLine.GetLength();
1790 CString sLine = GetLineChars(nLineIndex);
1791 int nLineLength = sLine.GetLength();
1792 CString sLineExp = ExpandChars(sLine);
1793 LPCTSTR textExp = sLineExp;
1794 //int nLineLengthExp = sLineExp.GetLength();
1795 int nStartExp = 0;
1796 int nLeft = coords.x;
1797 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1799 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1800 ++itEnd;
1801 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1802 int nEnd = nLineLength;
1803 if (itEnd != lineCols.end())
1805 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1807 int nBlockLength = nEnd - nStart;
1808 if (nBlockLength > 0 && nEnd>=0)
1810 pDC->SetBkColor(itStart->second.background);
1811 pDC->SetTextColor(itStart->second.text);
1812 int nEndExp = CountExpandedChars(sLine, nEnd);
1813 int nTextLength = nEndExp - nStartExp;
1814 LPCTSTR p_zBlockText = textExp + nStartExp;
1815 SIZE Size;
1816 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1817 int nRight = nLeft + Size.cx;
1818 if ((nRight > rc.left) && (nLeft < rc.right))
1820 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1821 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1822 // is 4094 (4095 doesn't work anymore).
1823 // So we limit the length here to that 4094 chars.
1824 // In case we're scrolled to the right, there's no need to draw the string
1825 // from way outside our window, so we also offset the drawing to the start of the window.
1826 // This reduces the string length as well.
1827 int offset = 0;
1828 int leftcoord = nLeft;
1829 if (nLeft < 0)
1831 offset = (-nLeft/GetCharWidth());
1832 nTextLength -= offset;
1833 leftcoord = nLeft % GetCharWidth();
1836 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1837 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1839 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1842 nLeft = nRight;
1843 coords.x = nRight;
1844 nStartExp = nEndExp;
1849 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1851 if (nLineIndex >= GetLineCount())
1852 nLineIndex = -1;
1853 ASSERT(nLineIndex >= -1);
1855 if ((nLineIndex == -1) || !m_pViewData)
1857 // Draw line beyond the text
1858 COLORREF crBkgnd, crText;
1859 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1860 pDC->FillSolidRect(rc, crBkgnd);
1861 return;
1864 int viewLine = GetViewLineForScreen(nLineIndex);
1865 if (m_pMainFrame->m_bCollapsed)
1867 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1869 COLORREF crBkgnd, crText;
1870 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1871 pDC->FillSolidRect(rc, crBkgnd);
1873 const int THICKNESS = 2;
1874 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1875 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1876 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1877 pDC->SetBkColor(crBkgnd);
1878 CRect rect = rc;
1879 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1880 return;
1884 DiffStates diffState = m_pViewData->GetState(viewLine);
1885 COLORREF crBkgnd, crText;
1886 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1888 if (diffState == DIFFSTATE_CONFLICTED)
1890 // conflicted lines are shown without 'text' on them
1891 CRect rect = rc;
1892 pDC->FillSolidRect(rc, crBkgnd);
1893 // now draw some faint text patterns
1894 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1895 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1896 DrawBlockLine(pDC, rc, nLineIndex);
1897 return;
1900 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1901 CString sLine = GetLineChars(nLineIndex);
1902 if (sLine.IsEmpty())
1904 pDC->FillSolidRect(rc, crBkgnd);
1905 DrawBlockLine(pDC, rc, nLineIndex);
1906 DrawLineEnding(pDC, rc, nLineIndex, origin);
1907 return;
1910 CheckOtherView();
1912 // Draw the line
1914 pDC->SelectObject(GetFont(FALSE, FALSE));
1916 DrawTextLine(pDC, rc, nLineIndex, origin);
1918 // draw white space after the end of line
1919 CRect frect = rc;
1920 if (origin.x > frect.left)
1921 frect.left = origin.x;
1922 if (frect.right > frect.left)
1923 pDC->FillSolidRect(frect, crBkgnd);
1925 // draw the whitespace chars
1926 LPCTSTR pszChars = (LPCWSTR)sLine;
1927 if (m_bViewWhitespace)
1929 int xpos = 0;
1930 int y = rc.top + (rc.bottom-rc.top)/2;
1932 int nActualOffset = 0;
1933 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1935 if (*pszChars == _T('\t'))
1936 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1937 else
1938 nActualOffset++;
1939 pszChars++;
1941 if (nActualOffset > m_nOffsetChar)
1942 pszChars--;
1944 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1945 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1946 while (*pszChars)
1948 switch (*pszChars)
1950 case _T('\t'):
1952 // draw an arrow
1953 CPen * oldPen = pDC->SelectObject(&pen);
1954 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1955 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1956 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1957 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1958 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1959 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1960 xpos += nSpaces;
1961 pDC->SelectObject(oldPen);
1963 break;
1964 case _T(' '):
1966 // draw a small dot
1967 CPen * oldPen = pDC->SelectObject(&pen2);
1968 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1969 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1970 xpos++;
1971 pDC->SelectObject(oldPen);
1973 break;
1974 default:
1975 xpos++;
1976 break;
1978 pszChars++;
1981 DrawBlockLine(pDC, rc, nLineIndex);
1982 if (origin.x >= rc.left)
1983 DrawLineEnding(pDC, rc, nLineIndex, origin);
1986 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1988 if (nCount <= 0)
1990 line = _T("");
1991 return;
1994 int nTabSize = GetTabSize();
1996 int nActualOffset = CountExpandedChars(sLine, nOffset);
1998 LPCTSTR pszChars = (LPCWSTR)sLine;
1999 pszChars += nOffset;
2000 int nLength = nCount;
2002 int nTabCount = 0;
2003 for (int i=0; i<nLength; i++)
2005 if (pszChars[i] == _T('\t'))
2006 nTabCount ++;
2009 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2010 int nCurPos = 0;
2011 if (nTabCount > 0 || m_bViewWhitespace)
2013 for (int i=0; i<nLength; i++)
2015 if (pszChars[i] == _T('\t'))
2017 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2018 while (nSpaces > 0)
2020 pszBuf[nCurPos ++] = _T(' ');
2021 nSpaces --;
2024 else
2026 pszBuf[nCurPos] = pszChars[i];
2027 nCurPos ++;
2031 else
2033 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2034 nCurPos = nLength;
2036 pszBuf[nCurPos] = 0;
2037 line.ReleaseBuffer();
2040 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2042 CString sRet;
2043 int nLength = sLine.GetLength();
2044 ExpandChars(sLine, nOffset, nLength, sRet);
2045 return sRet;
2048 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2050 int nTabSize = GetTabSize();
2052 int nActualOffset = 0;
2053 for (int i=0; i<nLength; i++)
2055 if (sLine[i] == _T('\t'))
2056 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2057 else
2058 nActualOffset ++;
2060 return nActualOffset;
2063 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2065 if (m_pwndLeft)
2066 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2067 if (m_pwndRight)
2068 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2069 if (m_pwndBottom)
2070 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2071 if (m_pwndLocator)
2072 m_pwndLocator->Invalidate();
2075 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2077 //almost the same as ScrollAllToLine, but try to put the line in the
2078 //middle of the view, not on top
2079 int nNewTopLine = nNewLine - GetScreenLines()/2;
2080 if (nNewTopLine < 0)
2081 nNewTopLine = 0;
2082 if (nNewTopLine >= (int)m_Screen2View.size())
2083 nNewTopLine = (int)m_Screen2View.size()-1;
2084 if (bAll)
2085 ScrollAllToLine(nNewTopLine);
2086 else
2087 ScrollToLine(nNewTopLine);
2090 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2092 return TRUE;
2095 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2097 if (CView::OnCreate(lpCreateStruct) == -1)
2098 return -1;
2100 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2101 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2102 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2103 m_lfBaseFont.lfHeight = 0;
2104 m_lfBaseFont.lfWeight = FW_NORMAL;
2105 m_lfBaseFont.lfItalic = FALSE;
2106 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2107 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2108 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2109 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2110 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2112 return 0;
2115 void CBaseView::OnDestroy()
2117 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2119 m_pFindDialog->SendMessage(WM_CLOSE);
2120 return;
2122 CView::OnDestroy();
2123 DeleteFonts();
2124 ReleaseBitmap();
2127 void CBaseView::OnSize(UINT nType, int cx, int cy)
2129 CView::OnSize(nType, cx, cy);
2130 ReleaseBitmap();
2132 m_nScreenLines = -1;
2133 m_nScreenChars = -1;
2134 if (m_nLastScreenChars != GetScreenChars())
2136 BuildAllScreen2ViewVector();
2137 m_nLastScreenChars = m_nScreenChars;
2138 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2140 // if we're in wrap mode, the line wrapping most likely changed
2141 // and that means we have to redraw the whole window, not just the
2142 // scrolled part.
2143 Invalidate(FALSE);
2145 else
2147 // make sure the view header is redrawn
2148 CRect rcScroll;
2149 GetClientRect(&rcScroll);
2150 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2151 InvalidateRect(&rcScroll, FALSE);
2154 else
2156 // make sure the view header is redrawn
2157 CRect rcScroll;
2158 GetClientRect(&rcScroll);
2159 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2160 InvalidateRect(&rcScroll, FALSE);
2162 UpdateLocator();
2163 RecalcVertScrollBar();
2164 RecalcHorzScrollBar();
2166 UpdateCaret();
2169 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2171 if (m_pwndLeft)
2172 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2173 if (m_pwndRight)
2174 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2175 if (m_pwndBottom)
2176 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2177 if (m_pwndLocator)
2178 m_pwndLocator->Invalidate();
2179 return CView::OnMouseWheel(nFlags, zDelta, pt);
2182 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2184 if (m_pwndLeft)
2185 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2186 if (m_pwndRight)
2187 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2188 if (m_pwndBottom)
2189 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2190 if (m_pwndLocator)
2191 m_pwndLocator->Invalidate();
2194 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2196 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2197 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2199 if (bControl || bShift)
2201 if (m_pMainFrame->m_bWrapLines)
2202 return;
2203 // Ctrl-Wheel scrolls sideways
2204 ScrollSide(-zDelta/30);
2206 else
2208 ScrollVertical(zDelta);
2212 void CBaseView::OnDoMouseHWheel(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 ScrollVertical(zDelta);
2221 else
2223 if (m_pMainFrame->m_bWrapLines)
2224 return;
2225 // Ctrl-Wheel scrolls sideways
2226 ScrollSide(-zDelta/30);
2230 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2232 if (nHitTest == HTCLIENT)
2234 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2236 if (m_nMouseLine < (int)m_Screen2View.size())
2238 if (m_nMouseLine >= 0)
2240 int viewLine = GetViewLineForScreen(m_nMouseLine);
2241 if (viewLine < m_pViewData->GetCount())
2243 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2245 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2246 return TRUE;
2252 if (m_mouseInMargin)
2254 ::SetCursor(m_margincursor);
2255 return TRUE;
2257 if (m_nMouseLine >= 0)
2259 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2260 return TRUE;
2263 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2264 return TRUE;
2266 return CView::OnSetCursor(pWnd, nHitTest, message);
2269 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2271 CView::OnKillFocus(pNewWnd);
2272 m_bFocused = FALSE;
2273 UpdateCaret();
2274 Invalidate();
2277 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2279 CView::OnSetFocus(pOldWnd);
2280 m_bFocused = TRUE;
2281 UpdateCaret();
2282 Invalidate();
2285 int CBaseView::GetLineFromPoint(CPoint point)
2287 ScreenToClient(&point);
2288 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2291 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2293 if (!this->IsWindowVisible())
2294 return;
2296 CIconMenu popup;
2297 if (!popup.CreatePopupMenu())
2298 return;
2300 AddContextItems(popup, state);
2302 CMenu popupEols;
2303 CMenu popupUnicode;
2304 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2305 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2306 if (IsWritable())
2308 CString temp;
2309 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2310 temp.LoadString(IDS_EDIT_TAB2SPACE);
2311 popup.AppendMenu(MF_STRING | oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2312 temp.LoadString(IDS_EDIT_SPACE2TAB);
2313 popup.AppendMenu(MF_STRING | oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2314 temp.LoadString(IDS_EDIT_TRIM);
2315 popup.AppendMenu(MF_STRING | oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2317 // add eol submenu
2318 if (!popupEols.CreatePopupMenu())
2319 return;
2321 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2322 for (int i = 1; i < _countof(eolArray); i++)
2324 CString temp = GetEolName(eolArray[i]);
2325 bool bChecked = (eEolType == eolArray[i]);
2326 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2329 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2330 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2332 // add encoding submenu
2333 if (!popupUnicode.CreatePopupMenu())
2334 return;
2335 for (int i = 0; i < _countof(uctArray); i++)
2337 CString temp = CFileTextLines::GetEncodingName(uctArray[i]);
2338 bool bChecked = (m_texttype == uctArray[i]);
2339 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2341 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2342 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2346 CompensateForKeyboard(point);
2348 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2349 ResetUndoStep();
2350 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2352 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2354 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2356 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2357 SaveUndoStep();
2359 switch (cmd)
2361 // 2-pane view commands; target is right view
2362 case POPUPCOMMAND_USELEFTBLOCK:
2363 m_pwndRight->UseLeftBlock();
2364 break;
2365 case POPUPCOMMAND_USELEFTFILE:
2366 m_pwndRight->UseLeftFile();
2367 break;
2368 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2369 m_pwndRight->UseBothLeftFirst();
2370 break;
2371 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2372 m_pwndRight->UseBothRightFirst();
2373 break;
2374 case POPUPCOMMAND_MARKBLOCK:
2375 m_pwndRight->MarkBlock(true);
2376 break;
2377 case POPUPCOMMAND_UNMARKBLOCK:
2378 m_pwndRight->MarkBlock(false);
2379 break;
2380 case POPUPCOMMAND_USELEFTFILEEXCEPTMARKED:
2381 m_pwndRight->UseLeftFileExceptMarked();
2382 break;
2383 // 2-pane view multiedit commands; target is left view
2384 case POPUPCOMMAND_PREPENDFROMRIGHT:
2385 if (!m_pwndLeft->IsReadonly())
2386 m_pwndLeft->UseBothRightFirst();
2387 break;
2388 case POPUPCOMMAND_REPLACEBYRIGHT:
2389 if (!m_pwndLeft->IsReadonly())
2390 m_pwndLeft->UseRightBlock();
2391 break;
2392 case POPUPCOMMAND_APPENDFROMRIGHT:
2393 if (!m_pwndLeft->IsReadonly())
2394 m_pwndLeft->UseBothLeftFirst();
2395 break;
2396 case POPUPCOMMAND_USERIGHTFILE:
2397 m_pwndLeft->UseRightFile();
2398 break;
2399 // 3-pane view commands; target is bottom view
2400 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2401 m_pwndBottom->UseBothRightFirst();
2402 break;
2403 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2404 m_pwndBottom->UseBothLeftFirst();
2405 break;
2406 case POPUPCOMMAND_USEYOURBLOCK:
2407 m_pwndBottom->UseRightBlock();
2408 break;
2409 case POPUPCOMMAND_USEYOURFILE:
2410 m_pwndBottom->UseRightFile();
2411 break;
2412 case POPUPCOMMAND_USETHEIRBLOCK:
2413 m_pwndBottom->UseLeftBlock();
2414 break;
2415 case POPUPCOMMAND_USETHEIRFILE:
2416 m_pwndBottom->UseLeftFile();
2417 break;
2418 // copy, cut and paste commands
2419 case ID_EDIT_COPY:
2420 OnEditCopy();
2421 break;
2422 case ID_EDIT_CUT:
2423 OnEditCut();
2424 break;
2425 case ID_EDIT_PASTE:
2426 OnEditPaste();
2427 break;
2428 // white chars manipulations
2429 case POPUPCOMMAND_TABTOSPACES:
2430 ConvertTabToSpaces();
2431 break;
2432 case POPUPCOMMAND_SPACESTOTABS:
2433 Tabularize();
2434 break;
2435 case POPUPCOMMAND_REMOVETRAILWHITES:
2436 RemoveTrailWhiteChars();
2437 break;
2438 default:
2439 return;
2440 } // switch (cmd)
2441 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2442 return;
2445 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2447 if (!m_pViewData)
2448 return;
2450 int nViewBlockStart = -1;
2451 int nViewBlockEnd = -1;
2452 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2453 if ((point.x >= 0) && (point.y >= 0))
2455 int nLine = GetLineFromPoint(point)-1;
2456 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2458 int nViewLine = GetViewLineForScreen(nLine);
2459 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2461 ClearSelection(); // Clear text-copy selection
2463 nViewBlockStart = nViewLine;
2464 nViewBlockEnd = nViewLine;
2465 DiffStates state = m_pViewData->GetState(nViewLine);
2466 while (nViewBlockStart > 0)
2468 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2469 if (!LinesInOneChange(-1, state, lineState))
2470 break;
2471 nViewBlockStart--;
2474 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2476 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2477 if (!LinesInOneChange(1, state, lineState))
2478 break;
2479 nViewBlockEnd++;
2482 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2483 UpdateCaretPosition(point);
2488 // FixSelection(); fix selection range
2489 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2490 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2492 DiffStates state = DIFFSTATE_UNKNOWN;
2493 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2495 // find a more 'relevant' state in the selection
2496 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2498 state = m_pViewData->GetState(i);
2499 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2500 break;
2503 OnContextMenu(point, state);
2506 void CBaseView::RefreshViews()
2508 if (m_pwndLeft)
2510 m_pwndLeft->UpdateStatusBar();
2511 m_pwndLeft->Invalidate();
2513 if (m_pwndRight)
2515 m_pwndRight->UpdateStatusBar();
2516 m_pwndRight->Invalidate();
2518 if (m_pwndBottom)
2520 m_pwndBottom->UpdateStatusBar();
2521 m_pwndBottom->Invalidate();
2523 if (m_pwndLocator)
2524 m_pwndLocator->Invalidate();
2527 void CBaseView::GoToFirstDifference()
2529 SetCaretToFirstViewLine();
2530 SelectNextBlock(1, false, false);
2533 void CBaseView::GoToFirstConflict()
2535 SetCaretToFirstViewLine();
2536 SelectNextBlock(1, true, false);
2539 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2541 ClearSelection();
2542 SetupAllSelection(nStart, max(nStart, nEnd));
2544 UpdateCaretPosition(SetupPoint(0, nStart));
2545 Invalidate();
2548 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2550 ClearSelection();
2551 SetupAllViewSelection(nStart, max(nStart, nEnd));
2553 UpdateCaretViewPosition(SetupPoint(0, nStart));
2554 Invalidate();
2557 void CBaseView::SetupAllViewSelection(int start, int end)
2559 SetupViewSelection(m_pwndBottom, start, end);
2560 SetupViewSelection(m_pwndLeft, start, end);
2561 SetupViewSelection(m_pwndRight, start, end);
2564 void CBaseView::SetupAllSelection(int start, int end)
2566 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2569 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2571 void CBaseView::SetupSelection(int start, int end)
2573 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2576 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2578 if (!IsViewGood(view))
2579 return;
2580 view->SetupViewSelection(start, end);
2583 void CBaseView::SetupViewSelection(int start, int end)
2585 // clear text selection before setting line selection ?
2586 m_nSelViewBlockStart = start;
2587 m_nSelViewBlockEnd = end;
2588 Invalidate();
2592 void CBaseView::OnMergePreviousconflict()
2594 SelectNextBlock(-1, true);
2597 void CBaseView::OnMergeNextconflict()
2599 SelectNextBlock(1, true);
2602 void CBaseView::OnMergeNextdifference()
2604 SelectNextBlock(1, false);
2607 void CBaseView::OnMergePreviousdifference()
2609 SelectNextBlock(-1, false);
2612 bool CBaseView::HasNextConflict()
2614 return SelectNextBlock(1, true, true, true);
2617 bool CBaseView::HasPrevConflict()
2619 return SelectNextBlock(-1, true, true, true);
2622 bool CBaseView::HasNextDiff()
2624 return SelectNextBlock(1, false, true, true);
2627 bool CBaseView::HasPrevDiff()
2629 return SelectNextBlock(-1, false, true, true);
2632 bool CBaseView::LinesInOneChange(int direction,
2633 DiffStates initialLineState, DiffStates currentLineState)
2635 // Checks whether all the adjacent lines starting from the initial line
2636 // and up to the current line form the single change
2638 // First of all, if the two lines have identical states, they surely
2639 // belong to one change.
2640 if (initialLineState == currentLineState)
2641 return true;
2643 // Either we move down and initial line state is "added" or "removed" and
2644 // current line state is "empty"...
2645 if (direction > 0)
2647 if (currentLineState == DIFFSTATE_EMPTY)
2649 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2650 return true;
2652 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2653 return true;
2655 // ...or we move up and initial line state is "empty" and current line
2656 // state is "added" or "removed".
2657 if (direction < 0)
2659 if (initialLineState == DIFFSTATE_EMPTY)
2661 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2662 return true;
2664 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2665 return true;
2667 return false;
2670 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2672 if (! m_pViewData)
2673 return false;
2675 const int linesCount = (int)m_Screen2View.size();
2676 if(linesCount == 0)
2677 return false;
2679 int nCenterPos = GetCaretPosition().y;
2680 int nLimit = -1;
2681 if (nDirection > 0)
2682 nLimit = linesCount;
2684 if (nCenterPos >= linesCount)
2685 nCenterPos = linesCount-1;
2687 if (bSkipEndOfCurrentBlock)
2689 // Find end of current block
2690 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2691 while (nCenterPos != nLimit)
2693 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2694 if (!LinesInOneChange(nDirection, state, lineState))
2695 break;
2696 nCenterPos += nDirection;
2700 // Find next diff/conflict block
2701 while (nCenterPos != nLimit)
2703 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2704 if (!bConflict &&
2705 (linestate != DIFFSTATE_NORMAL) &&
2706 (linestate != DIFFSTATE_UNKNOWN))
2708 break;
2710 if (bConflict &&
2711 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2712 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2713 (linestate == DIFFSTATE_CONFLICTED) ||
2714 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2716 break;
2719 nCenterPos += nDirection;
2721 if (nCenterPos == nLimit)
2722 return false;
2723 if (dryrun)
2724 return (nCenterPos != nLimit);
2726 // Find end of new block
2727 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2728 int nBlockEnd = nCenterPos;
2729 const int maxAllowedLine = nLimit-nDirection;
2730 while (nBlockEnd != maxAllowedLine)
2732 const int lineIndex = nBlockEnd + nDirection;
2733 if (lineIndex >= linesCount)
2734 break;
2735 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2736 if (!LinesInOneChange(nDirection, state, lineState))
2737 break;
2738 nBlockEnd += nDirection;
2741 int nTopPos = nCenterPos - (GetScreenLines()/2);
2742 if (nTopPos < 0)
2743 nTopPos = 0;
2745 POINT ptCaretPos = {0, nCenterPos};
2746 SetCaretPosition(ptCaretPos);
2747 ClearSelection();
2748 if (nDirection > 0)
2749 SetupAllSelection(nCenterPos, nBlockEnd);
2750 else
2751 SetupAllSelection(nBlockEnd, nCenterPos);
2753 ScrollAllToLine(nTopPos, FALSE);
2754 RecalcAllVertScrollBars(TRUE);
2755 SetCaretToLineStart();
2756 EnsureCaretVisible();
2757 OnNavigateNextinlinediff();
2759 UpdateViewsCaretPosition();
2760 UpdateCaret();
2761 ShowDiffLines(nCenterPos);
2762 return true;
2765 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2767 if (pNMHDR->idFrom != (UINT)m_hWnd)
2768 return FALSE;
2770 CString strTipText;
2771 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2773 DWORD pos = GetMessagePos();
2774 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2775 ScreenToClient(&point);
2776 const int nLine = GetButtonEventLineIndex(point);
2778 if (nLine >= 0)
2780 int nViewLine = GetViewLineForScreen(nLine);
2781 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2783 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2784 if (movedIndex >= 0)
2786 if (m_pViewData->IsMovedFrom(nViewLine))
2788 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2790 else
2792 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2799 *pResult = 0;
2800 if (strTipText.IsEmpty())
2801 return TRUE;
2803 // need to handle both ANSI and UNICODE versions of the message
2804 if (pNMHDR->code == TTN_NEEDTEXTA)
2806 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2807 pTTTA->lpszText = m_szTip;
2808 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2810 else
2812 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2813 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2814 pTTTW->lpszText = m_wszTip;
2817 return TRUE; // message was handled
2820 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2822 CRect rcClient;
2823 GetClientRect(rcClient);
2824 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2825 int marginwidth = MARGINWIDTH;
2826 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2828 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2830 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2832 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2834 // inside the header part of the view (showing the filename)
2835 pTI->hwnd = this->m_hWnd;
2836 this->GetClientRect(&pTI->rect);
2837 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2838 pTI->uId = (UINT)m_hWnd;
2839 pTI->lpszText = LPSTR_TEXTCALLBACK;
2841 // we want multi line tooltips
2842 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2843 if (pToolTip->GetSafeHwnd() != NULL)
2845 pToolTip->SetMaxTipWidth(INT_MAX);
2848 return (textrect.PtInRect(point) ? 1 : 2);
2851 return -1;
2854 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2856 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2857 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2859 switch (nChar)
2861 case VK_TAB:
2862 if (bControl)
2864 if (this==m_pwndLeft)
2866 if (IsViewGood(m_pwndRight))
2868 m_pwndRight->SetFocus();
2870 else if (IsViewGood(m_pwndBottom))
2872 m_pwndBottom->SetFocus();
2875 else if (this==m_pwndRight)
2877 if (IsViewGood(m_pwndBottom))
2879 m_pwndBottom->SetFocus();
2881 else if (IsViewGood(m_pwndLeft))
2883 m_pwndLeft->SetFocus();
2886 else if (this==m_pwndBottom)
2888 if (IsViewGood(m_pwndLeft))
2890 m_pwndLeft->SetFocus();
2892 else if (IsViewGood(m_pwndRight))
2894 m_pwndRight->SetFocus();
2898 break;
2899 case VK_PRIOR:
2901 POINT ptCaretPos = GetCaretPosition();
2902 ptCaretPos.y -= GetScreenLines();
2903 ptCaretPos.y = max(ptCaretPos.y, 0);
2904 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2905 SetCaretPosition(ptCaretPos);
2906 OnCaretMove(MOVELEFT, bShift);
2907 ShowDiffLines(ptCaretPos.y);
2909 break;
2910 case VK_NEXT:
2912 POINT ptCaretPos = GetCaretPosition();
2913 ptCaretPos.y += GetScreenLines();
2914 if (ptCaretPos.y >= GetLineCount())
2915 ptCaretPos.y = GetLineCount()-1;
2916 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2917 SetCaretPosition(ptCaretPos);
2918 OnCaretMove(MOVERIGHT, bShift);
2919 ShowDiffLines(ptCaretPos.y);
2921 break;
2922 case VK_HOME:
2924 if (bControl)
2926 ScrollAllToLine(0);
2927 SetCaretToViewStart();
2928 m_nCaretGoalPos = 0;
2929 if (bShift)
2930 AdjustSelection(MOVELEFT);
2931 else
2932 ClearSelection();
2933 UpdateCaret();
2935 else
2937 POINT ptCaretPos = GetCaretPosition();
2938 const CString &sLine = GetViewLine(ptCaretPos.y);
2939 int pos = 0;
2940 while (pos < sLine.GetLength())
2942 if (sLine[pos] != ' ' && sLine[pos] != '\t')
2943 break;
2944 ++pos;
2946 if (ptCaretPos.x == pos)
2948 SetCaretToLineStart();
2949 m_nCaretGoalPos = 0;
2950 OnCaretMove(MOVERIGHT, bShift);
2951 ScrollAllToChar(0);
2953 else
2955 ptCaretPos.x = pos;
2956 SetCaretAndGoalPosition(ptCaretPos);
2957 OnCaretMove(bShift);
2961 break;
2962 case VK_END:
2964 if (bControl)
2966 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2967 POINT ptCaretPos;
2968 ptCaretPos.y = GetLineCount()-1;
2969 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2970 SetCaretAndGoalPosition(ptCaretPos);
2971 if (bShift)
2972 AdjustSelection(MOVERIGHT);
2973 else
2974 ClearSelection();
2976 else
2978 POINT ptCaretPos = GetCaretPosition();
2979 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2980 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2982 ptCaretPos.x--;
2984 SetCaretAndGoalPosition(ptCaretPos);
2985 OnCaretMove(bShift);
2988 break;
2989 case VK_BACK:
2990 if (IsWritable())
2992 if (! HasTextSelection())
2994 POINT ptCaretPos = GetCaretPosition();
2995 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2996 break;
2997 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2998 if (bControl)
2999 MoveCaretWordLeft();
3000 else
3002 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3006 m_ptSelectionViewPosStart = GetCaretViewPosition();
3008 RemoveSelectedText();
3010 break;
3011 case VK_DELETE:
3012 if (IsWritable())
3014 if (! HasTextSelection())
3016 if (bControl)
3018 m_ptSelectionViewPosStart = GetCaretViewPosition();
3019 MoveCaretWordRight();
3020 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3022 else
3024 if (! MoveCaretRight())
3025 break;
3026 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3027 MoveCaretLeft();
3028 m_ptSelectionViewPosStart = GetCaretViewPosition();
3031 RemoveSelectedText();
3033 break;
3035 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3038 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3040 const int nClickedLine = GetButtonEventLineIndex(point);
3041 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3043 POINT ptCaretPos;
3044 ptCaretPos.y = nClickedLine;
3045 LONG xpos = point.x - GetMarginWidth();
3046 LONG xpos2 = xpos / GetCharWidth();
3047 xpos2 += m_nOffsetChar;
3048 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3049 xpos2++;
3050 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3051 SetCaretAndGoalPosition(ptCaretPos);
3053 if (nFlags & MK_SHIFT)
3054 AdjustSelection(MOVERIGHT);
3055 else
3057 ClearSelection();
3058 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3059 if (point.x < GetMarginWidth())
3061 // select the whole line
3062 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3063 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3067 UpdateViewsCaretPosition();
3068 Invalidate();
3071 CView::OnLButtonDown(nFlags, point);
3074 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3076 if (zChar == ' ' || zChar == '\t' )
3078 return CHG_WHITESPACE;
3080 if (zChar < 0x20)
3082 return CHG_CONTROL;
3084 if (m_sWordSeparators.Find(zChar) >= 0)
3086 return CHG_WORDSEPARATOR;
3088 return CHG_WORDLETTER;
3091 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3093 if (m_pViewData == 0) {
3094 CView::OnLButtonDblClk(nFlags, point);
3095 return;
3098 const int nClickedLine = GetButtonEventLineIndex(point);
3099 if ( nClickedLine < 0)
3100 return;
3101 int nViewLine = GetViewLineForScreen(nClickedLine);
3102 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3104 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3106 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3108 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3109 int screenLine = FindViewLineNumber(movedindex);
3110 int nTop = screenLine - GetScreenLines()/2;
3111 if (nTop < 0)
3112 nTop = 0;
3113 ScrollAllToLine(nTop);
3114 // find and select the whole moved block
3115 int startSel = movedindex;
3116 int endSel = movedindex;
3117 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3118 startSel--;
3119 startSel++;
3120 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3121 endSel++;
3122 endSel--;
3123 m_pOtherView->SetupSelection(startSel, endSel);
3124 return CView::OnLButtonDblClk(nFlags, point);
3128 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3130 // a double click on a marker expands the hidden text
3131 int i = nViewLine;
3132 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3134 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3135 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3136 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3137 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3138 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3139 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3140 i++;
3142 BuildAllScreen2ViewVector();
3143 if (m_pwndLeft)
3144 m_pwndLeft->Invalidate();
3145 if (m_pwndRight)
3146 m_pwndRight->Invalidate();
3147 if (m_pwndBottom)
3148 m_pwndBottom->Invalidate();
3150 else
3152 POINT ptCaretPos;
3153 ptCaretPos.y = nClickedLine;
3154 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3155 SetCaretPosition(ptCaretPos);
3156 ClearSelection();
3158 POINT ptViewCarret = GetCaretViewPosition();
3159 nViewLine = ptViewCarret.y;
3160 if (nViewLine >= GetViewCount())
3161 return;
3162 const CString &sLine = GetViewLine(nViewLine);
3163 int nLineLength = sLine.GetLength();
3164 int nBasePos = ptViewCarret.x;
3165 // get target char group
3166 ECharGroup eLeft = CHG_UNKNOWN;
3167 if (nBasePos > 0)
3169 eLeft = GetCharGroup(sLine[nBasePos-1]);
3171 ECharGroup eRight = CHG_UNKNOWN;
3172 if (nBasePos < nLineLength)
3174 eRight = GetCharGroup(sLine[nBasePos]);
3176 ECharGroup eTarget = max(eRight, eLeft);
3177 // find left margin
3178 int nLeft = nBasePos;
3179 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3181 nLeft--;
3183 // get right margin
3184 int nRight = nBasePos;
3185 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3187 nRight++;
3189 // set selection
3190 m_ptSelectionViewPosStart.x = nLeft;
3191 m_ptSelectionViewPosStart.y = nViewLine;
3192 m_ptSelectionViewPosEnd.x = nRight;
3193 m_ptSelectionViewPosEnd.y = nViewLine;
3194 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3195 SetupAllViewSelection(nViewLine, nViewLine);
3196 // set caret
3197 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3198 UpdateViewsCaretPosition();
3199 UpdateGoalPos();
3201 // set mark word
3202 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3203 int nMarkWidth = max(nRight - nLeft, 0);
3204 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3205 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3207 m_sMarkedWord.Empty();
3210 if (m_pwndLeft)
3211 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3212 if (m_pwndRight)
3213 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3214 if (m_pwndBottom)
3215 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3217 Invalidate();
3218 if (m_pwndLocator)
3219 m_pwndLocator->Invalidate();
3222 CView::OnLButtonDblClk(nFlags, point);
3225 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3227 const int nClickedLine = GetButtonEventLineIndex(point);
3228 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3230 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3232 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3233 if (pidl)
3235 SHOpenFolderAndSelectItems(pidl,0,0,0);
3236 CoTaskMemFree((LPVOID)pidl);
3239 return;
3241 POINT ptCaretPos;
3242 ptCaretPos.y = nClickedLine;
3243 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3244 SetCaretAndGoalPosition(ptCaretPos);
3245 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3246 if (m_pwndLeft)
3247 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3248 if (m_pwndRight)
3249 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3250 if (m_pwndBottom)
3251 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3252 ClearSelection();
3253 m_ptSelectionViewPosStart.x = 0;
3254 m_ptSelectionViewPosStart.y = nClickedLine;
3255 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3256 m_ptSelectionViewPosEnd.y = nClickedLine;
3257 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3258 UpdateViewsCaretPosition();
3259 Invalidate();
3260 if (m_pwndLocator)
3261 m_pwndLocator->Invalidate();
3264 void CBaseView::OnEditCopy()
3266 CString sCopyData = GetSelectedText();
3268 if (!sCopyData.IsEmpty())
3270 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3274 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3276 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3278 --m_pMainFrame->m_nMoveMovesToIgnore;
3279 CView::OnMouseMove(nFlags, point);
3280 return;
3282 int nMouseLine = GetButtonEventLineIndex(point);
3283 if (nMouseLine < -1)
3284 nMouseLine = -1;
3285 m_mouseInMargin = point.x < GetMarginWidth();
3287 ShowDiffLines(nMouseLine);
3289 KillTimer(IDT_SCROLLTIMER);
3290 if (nFlags & MK_LBUTTON)
3292 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3293 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3294 if (saveMouseLine < 0)
3295 return;
3296 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3297 if (HasSelection() &&
3298 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3300 POINT ptCaretPos = {charIndex, nMouseLine};
3301 SetCaretAndGoalPosition(ptCaretPos);
3302 AdjustSelection(MOVERIGHT);
3303 Invalidate();
3304 UpdateWindow();
3306 if (nMouseLine < m_nTopLine)
3308 ScrollAllToLine(m_nTopLine-1, TRUE);
3309 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3311 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3313 ScrollAllToLine(m_nTopLine+1, TRUE);
3314 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3316 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3318 ScrollAllSide(-1);
3319 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3321 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3323 ScrollAllSide(1);
3324 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3326 SetCapture();
3330 CView::OnMouseMove(nFlags, point);
3333 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3335 ShowDiffLines(-1);
3336 ReleaseCapture();
3337 KillTimer(IDT_SCROLLTIMER);
3339 __super::OnLButtonUp(nFlags, point);
3342 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3344 if (nIDEvent == IDT_SCROLLTIMER)
3346 POINT point;
3347 GetCursorPos(&point);
3348 ScreenToClient(&point);
3349 int nMouseLine = GetButtonEventLineIndex(point);
3350 if (nMouseLine < -1)
3352 nMouseLine = -1;
3354 if (GetKeyState(VK_LBUTTON)&0x8000)
3356 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3357 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3358 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3359 if (nMouseLine < m_nTopLine)
3361 ScrollAllToLine(m_nTopLine-1, TRUE);
3362 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3364 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3366 ScrollAllToLine(m_nTopLine+1, TRUE);
3367 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3369 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3371 ScrollAllSide(-1);
3372 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3374 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3376 ScrollAllSide(1);
3377 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3383 CView::OnTimer(nIDEvent);
3386 void CBaseView::ShowDiffLines(int nLine)
3388 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3390 m_pwndLineDiffBar->ShowLines(nLine);
3391 nLine = -1;
3392 m_nMouseLine = nLine;
3393 return;
3396 if ((!m_pwndRight)||(!m_pwndLeft))
3397 return;
3398 if(m_pMainFrame->m_bOneWay)
3399 return;
3401 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3402 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3404 if (nLine < 0)
3405 return;
3407 if (nLine != m_nMouseLine)
3409 if (nLine >= GetLineCount())
3410 nLine = -1;
3411 m_nMouseLine = nLine;
3412 m_pwndLineDiffBar->ShowLines(nLine);
3414 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3417 const viewdata& CBaseView::GetEmptyLineData()
3419 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3420 return emptyLine;
3423 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3425 for (int i = 0; i < nCount; i++)
3427 InsertViewData(nFirstView, GetEmptyLineData());
3432 void CBaseView::UpdateCaret()
3434 POINT ptCaretPos = GetCaretPosition();
3435 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3436 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3437 SetCaretPosition(ptCaretPos);
3439 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3441 if (m_bFocused &&
3442 ptCaretPos.y >= m_nTopLine &&
3443 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3444 nCaretOffset >= m_nOffsetChar &&
3445 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3447 CreateSolidCaret(2, GetLineHeight());
3448 SetCaretPos(TextToClient(ptCaretPos));
3449 ShowCaret();
3451 else
3453 HideCaret();
3457 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3459 POINT ptViewPos;
3460 ptViewPos.x = pt.x;
3462 int nSubLine = GetSubLineOffset(pt.y);
3463 if (nSubLine > 0)
3465 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3467 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3471 ptViewPos.y = GetViewLineForScreen(pt.y);
3472 return ptViewPos;
3475 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3477 POINT ptPos;
3478 int nViewLineLenLeft = GetViewLineLength(pt.y);
3479 ptPos.x = min(nViewLineLenLeft, pt.x);
3480 ptPos.y = FindScreenLineForViewLine(pt.y);
3481 if (GetViewLineForScreen(ptPos.y) != pt.y )
3483 ptPos.x = 0;
3485 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3487 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3488 while (nSubLineLength < ptPos.x)
3490 ptPos.x -= nSubLineLength;
3491 nViewLineLenLeft -= nSubLineLength;
3492 ptPos.y++;
3493 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3495 // last pos of non last sub-line go to start of next screen line
3496 // Note: while this works correctly, it's not what a user might expect:
3497 // cursor-right when the caret is before the last char of a wrapped line
3498 // now moves the caret to the next line. But users expect the caret to
3499 // move to the right of the last char instead, and with another cursor-right
3500 // keystroke to move the caret to the next line.
3501 // Basically, this would require to handle two caret positions for the same
3502 // logical position in the line string (one on the last position of the first line,
3503 // one on the first position of the new line. For non-wrapped lines this works
3504 // because there's an 'invisible' newline char at the end of the first line.
3505 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3507 ptPos.x = 0;
3508 ptPos.y++;
3512 return ptPos;
3516 void CBaseView::EnsureCaretVisible()
3518 POINT ptCaretPos = GetCaretPosition();
3519 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3521 if (ptCaretPos.y < m_nTopLine)
3522 ScrollAllToLine(ptCaretPos.y);
3523 int screnLines = GetScreenLines();
3524 if (screnLines)
3526 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3527 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3528 if (nCaretOffset < m_nOffsetChar)
3529 ScrollAllToChar(nCaretOffset);
3530 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3531 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3535 int CBaseView::CalculateActualOffset(const POINT& point)
3537 int nLineIndex = point.y;
3538 int nCharIndex = point.x;
3539 ASSERT(nCharIndex >= 0);
3540 CString sLine = GetLineChars(nLineIndex);
3541 int nLineLength = sLine.GetLength();
3542 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3545 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3547 int nLength = GetLineLength(nLineIndex);
3548 int nSubLine = GetSubLineOffset(nLineIndex);
3549 if (nSubLine>=0)
3551 int nViewLine = GetViewLineForScreen(nLineIndex);
3552 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3554 int nMultilineCount = CountMultiLines(nViewLine);
3555 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3557 nLength--;
3561 CString Line = GetLineChars(nLineIndex);
3562 int nIndex = 0;
3563 int nOffset = 0;
3564 int nTabSize = GetTabSize();
3565 while (nOffset < nActualOffset && nIndex < nLength)
3567 if (Line.GetAt(nIndex) == _T('\t'))
3568 nOffset += (nTabSize - nOffset % nTabSize);
3569 else
3570 ++nOffset;
3571 ++nIndex;
3573 return nIndex;
3576 POINT CBaseView::TextToClient(const POINT& point)
3578 POINT pt;
3579 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3580 pt.y = nOffsetScreenLine * GetLineHeight();
3581 pt.x = CalculateActualOffset(point);
3583 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3584 CDC * pDC = GetDC();
3585 if (pDC)
3587 pDC->SelectObject(GetFont()); // is this right font ?
3588 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3589 CString sLine = GetLineChars(nScreenLine);
3590 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3591 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3592 ReleaseDC(pDC);
3593 } else {
3594 nLeft += pt.x * GetCharWidth();
3597 pt.x = nLeft;
3598 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3599 return pt;
3602 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3604 CView::OnChar(nChar, nRepCnt, nFlags);
3606 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3607 bool bSkipSelectionClear = false;
3609 if (IsReadonly())
3610 return;
3612 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3613 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3615 return;
3618 if (!m_pViewData) // no data - nothing to do
3619 return;
3621 if (nChar == VK_F16)
3623 // generated by a ctrl+backspace - ignore.
3625 else if (nChar==VK_TAB && HasTextLineSelection())
3627 // change indentation for selected lines
3628 if (bShift)
3630 RemoveIndentationForSelectedBlock();
3632 else
3634 AddIndentationForSelectedBlock();
3636 bSkipSelectionClear = true;
3638 else if ((nChar > 31)||(nChar == VK_TAB))
3640 ResetUndoStep();
3641 RemoveSelectedText();
3642 POINT ptCaretViewPos = GetCaretViewPosition();
3643 int nViewLine = ptCaretViewPos.y;
3644 if ((nViewLine==0)&&(GetViewCount()==0))
3645 OnChar(VK_RETURN, 0, 0);
3646 int charCount = 1;
3647 viewdata lineData = GetViewData(nViewLine);
3648 if (nChar == VK_TAB)
3650 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3651 if (indentChars > 0)
3653 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3654 charCount = indentChars;
3656 else
3657 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3659 else
3660 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3661 if (IsStateEmpty(lineData.state))
3663 // if not last line set EOL
3664 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3666 if (!IsViewLineEmpty(nCheckViewLine))
3668 lineData.ending = m_lineendings;
3669 break;
3672 // make sure previous (non empty) line have EOL set
3673 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3675 if (!IsViewLineEmpty(nCheckViewLine))
3677 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3679 SetViewLineEnding(nCheckViewLine, m_lineendings);
3681 break;
3685 lineData.state = DIFFSTATE_EDITED;
3686 bool bNeedRenumber = false;
3687 if (lineData.linenumber == -1)
3689 lineData.linenumber = 0;
3690 bNeedRenumber = true;
3692 SetViewData(nViewLine, lineData);
3693 SetModified();
3694 SaveUndoStep();
3695 BuildAllScreen2ViewVector(nViewLine);
3696 if (bNeedRenumber)
3698 UpdateViewLineNumbers();
3700 for (int i = 0; i < charCount; ++i)
3701 MoveCaretRight();
3702 UpdateGoalPos();
3704 else if (nChar == 10)
3706 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3707 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3708 EOL newEOL = EOL_CRLF;
3709 switch (eol)
3711 case EOL_CRLF:
3712 newEOL = EOL_CR;
3713 break;
3714 case EOL_CR:
3715 newEOL = EOL_LF;
3716 break;
3717 case EOL_LF:
3718 newEOL = EOL_CRLF;
3719 break;
3721 if (eol==EOL_NOENDING || eol==newEOL)
3722 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3723 // to add EOL on newly edited empty line hit enter
3724 // don't store into UNDO if no change happened
3725 // and don't mark file as modified
3726 return;
3727 AddUndoViewLine(nViewLine);
3728 m_pViewData->SetLineEnding(nViewLine, newEOL);
3729 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3730 UpdateGoalPos();
3732 else if (nChar == VK_RETURN)
3734 // insert a new, fresh and empty line below the cursor
3735 RemoveSelectedText();
3737 CUndo::GetInstance().BeginGrouping();
3739 POINT ptCaretViewPos = GetCaretViewPosition();
3740 int nViewLine = ptCaretViewPos.y;
3741 int nLeft = ptCaretViewPos.x;
3742 CString sLine = GetViewLineChars(nViewLine);
3743 CString sLineLeft = sLine.Left(nLeft);
3744 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3745 EOL eOriginalEnding = EOL_AUTOLINE;
3746 if (m_pViewData->GetCount() > nViewLine)
3747 eOriginalEnding = GetViewLineEnding(nViewLine);
3749 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3751 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3752 SetViewData(nViewLine, newFirstLine);
3755 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3756 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3757 InsertViewData(nInsertLine, newLastLine);
3758 SetModified();
3759 SaveUndoStep();
3761 // adds new line everywhere except me
3762 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3764 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3766 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3768 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3770 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3772 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3774 SaveUndoStep();
3776 UpdateViewLineNumbers();
3777 SaveUndoStep();
3778 CUndo::GetInstance().EndGrouping();
3780 BuildAllScreen2ViewVector();
3781 // move the cursor to the new line
3782 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3783 SetCaretAndGoalViewPosition(ptCaretViewPos);
3785 else
3786 return; // Unknown control character -- ignore it.
3787 if (!bSkipSelectionClear)
3788 ClearSelection();
3789 EnsureCaretVisible();
3790 UpdateCaret();
3791 Invalidate(FALSE);
3794 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3796 ResetUndoStep();
3797 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3798 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3799 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3800 SetModified();
3801 SaveUndoStep();
3802 RecalcAllVertScrollBars();
3803 Invalidate(FALSE);
3806 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3808 if (m_pViewData == NULL)
3809 return;
3810 int viewLine = nViewLineIndex;
3811 EOL ending = m_pViewData->GetLineEnding(viewLine);
3812 if (ending == EOL_NOENDING)
3814 ending = m_lineendings;
3816 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3817 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3819 CString sPartLine = GetViewLineChars(nViewLineIndex);
3820 int nPosx = GetCaretPosition().x; // should be view pos ?
3821 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3822 sPartLine = sPartLine.Mid(nPosx);
3823 newLine.sLine = sPartLine;
3825 m_pViewData->InsertData(viewLine+1, newLine);
3826 BuildAllScreen2ViewVector();
3829 void CBaseView::RemoveSelectedText()
3831 if (m_pViewData == NULL)
3832 return;
3833 if (!HasTextSelection())
3834 return;
3836 // fix selection if starts or ends on empty line
3837 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3838 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3841 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3842 SetCaretViewPosition(m_ptSelectionViewPosStart);
3843 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3846 m_ptSelectionViewPosStart = GetCaretViewPosition();
3847 if (!HasTextSelection())
3849 ClearSelection();
3850 return;
3853 // We want to undo the insertion in a single step.
3854 ResetUndoStep();
3855 CUndo::GetInstance().BeginGrouping();
3857 // combine first and last line
3858 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3859 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3860 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3861 oFirstLine.ending = oLastLine.ending;
3862 oFirstLine.state = DIFFSTATE_EDITED;
3863 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3865 // clean up middle lines if any
3866 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3868 viewdata oEmptyLine = GetEmptyLineData();
3869 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3871 SetViewData(nViewLine, oEmptyLine);
3873 SaveUndoStep();
3875 if (CleanEmptyLines())
3877 BuildAllScreen2ViewVector(); // schedule full rebuild
3879 SaveUndoStep();
3880 UpdateViewLineNumbers();
3883 SetModified(); //TODO set modified only if real data was changed
3884 SaveUndoStep();
3885 CUndo::GetInstance().EndGrouping();
3887 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3888 SetCaretViewPosition(m_ptSelectionViewPosStart);
3889 UpdateGoalPos();
3890 ClearSelection();
3891 UpdateCaret();
3892 EnsureCaretVisible();
3893 Invalidate(FALSE);
3896 void CBaseView::PasteText()
3898 if (!OpenClipboard())
3899 return;
3901 CString sClipboardText;
3902 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3903 if (hglb)
3905 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3906 sClipboardText = CString(lpstr);
3907 GlobalUnlock(hglb);
3909 hglb = GetClipboardData(CF_UNICODETEXT);
3910 if (hglb)
3912 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3913 sClipboardText = lpstr;
3914 GlobalUnlock(hglb);
3916 CloseClipboard();
3918 if (sClipboardText.IsEmpty())
3919 return;
3921 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3922 sClipboardText.Replace('\n', '\r');
3924 ResetUndoStep();
3926 POINT ptCaretViewPos = GetCaretViewPosition();
3927 int nLeft = ptCaretViewPos.x;
3928 int nViewLine = ptCaretViewPos.y;
3930 if ((nViewLine==0)&&(GetViewCount()==0))
3931 OnChar(VK_RETURN, 0, 0);
3933 std::vector<CString> lines;
3934 int nStart = 0;
3935 int nEolPos = 0;
3936 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3938 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3939 lines.push_back(sLine);
3940 nEolPos++;
3941 nStart = nEolPos;
3943 CString sLine = sClipboardText.Mid(nStart);
3944 lines.push_back(sLine);
3946 int nLinesToPaste = (int)lines.size();
3947 if (nLinesToPaste > 1)
3949 // multiline text
3951 // We want to undo the multiline insertion in a single step.
3952 CUndo::GetInstance().BeginGrouping();
3954 sLine = GetViewLineChars(nViewLine);
3955 CString sLineLeft = sLine.Left(nLeft);
3956 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3957 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3958 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3959 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3961 newLine.sLine = sLineLeft + lines[0];
3962 SetViewData(nViewLine, newLine);
3965 int nInsertLine = nViewLine;
3966 for (int i = 1; i < nLinesToPaste-1; i++)
3968 newLine.sLine = lines[i];
3969 InsertViewData(++nInsertLine, newLine);
3971 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3972 newLine.ending = eOriginalEnding;
3973 InsertViewData(++nInsertLine, newLine);
3975 SetModified();
3976 SaveUndoStep();
3978 // adds new lines everywhere except me
3979 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3981 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3983 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3985 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3987 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3989 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3991 SaveUndoStep();
3993 UpdateViewLineNumbers();
3994 CUndo::GetInstance().EndGrouping();
3996 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3998 else
4000 // single line text - just insert it
4001 sLine = GetViewLineChars(nViewLine);
4002 sLine.Insert(nLeft, sClipboardText);
4003 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
4004 SetViewLine(nViewLine, sLine);
4005 SetViewState(nViewLine, DIFFSTATE_EDITED);
4006 SetModified();
4007 SaveUndoStep();
4010 RefreshViews();
4011 BuildAllScreen2ViewVector();
4012 UpdateCaretViewPosition(ptCaretViewPos);
4015 void CBaseView::OnCaretDown()
4017 POINT ptCaretPos = GetCaretPosition();
4018 int nLine = ptCaretPos.y;
4019 int nNextLine = nLine + 1;
4020 if (nNextLine >= GetLineCount()) // already at last line
4022 return;
4025 POINT ptCaretViewPos = GetCaretViewPosition();
4026 int nViewLine = ptCaretViewPos.y;
4027 int nNextViewLine = GetViewLineForScreen(nNextLine);
4028 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
4030 // find next suitable screen line
4031 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
4033 nNextLine++;
4034 if (nNextLine >= GetLineCount())
4036 return;
4038 nNextViewLine = GetViewLineForScreen(nNextLine);
4041 ptCaretPos.y = nNextLine;
4042 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4043 SetCaretPosition(ptCaretPos);
4044 OnCaretMove(MOVELEFT);
4045 ShowDiffLines(ptCaretPos.y);
4048 bool CBaseView::MoveCaretLeft()
4050 POINT ptCaretViewPos = GetCaretViewPosition();
4052 //int nViewLine = ptCaretViewPos.y;
4053 if (ptCaretViewPos.x == 0)
4055 int nPrevLine = GetCaretPosition().y;
4056 int nPrevViewLine;
4057 do {
4058 nPrevLine--;
4059 if (nPrevLine < 0)
4061 return false;
4063 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4064 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4065 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4066 ShowDiffLines(nPrevLine);
4068 else
4069 --ptCaretViewPos.x;
4071 SetCaretAndGoalViewPosition(ptCaretViewPos);
4072 return true;
4075 bool CBaseView::MoveCaretRight()
4077 POINT ptCaretViewPos = GetCaretViewPosition();
4079 int nViewLine = ptCaretViewPos.y;
4080 int nViewLineLen = GetViewLineLength(nViewLine);
4081 if (ptCaretViewPos.x >= nViewLineLen)
4083 int nNextLine = GetCaretPosition().y;
4084 int nNextViewLine;
4085 do {
4086 nNextLine++;
4087 if (nNextLine >= GetLineCount())
4089 return false;
4091 nNextViewLine = GetViewLineForScreen(nNextLine);
4092 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4093 ptCaretViewPos.y = nNextViewLine;
4094 ptCaretViewPos.x = 0;
4095 ShowDiffLines(nNextLine);
4097 else
4098 ++ptCaretViewPos.x;
4100 SetCaretAndGoalViewPosition(ptCaretViewPos);
4101 return true;
4104 void CBaseView::UpdateGoalPos()
4106 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4109 void CBaseView::OnCaretLeft()
4111 MoveCaretLeft();
4112 OnCaretMove(MOVELEFT);
4115 void CBaseView::OnCaretRight()
4117 MoveCaretRight();
4118 OnCaretMove(MOVERIGHT);
4121 void CBaseView::OnCaretUp()
4123 POINT ptCaretPos = GetCaretPosition();
4124 int nLine = ptCaretPos.y;
4125 if (nLine <= 0) // already at first line
4127 return;
4129 int nPrevLine = nLine - 1;
4131 POINT ptCaretViewPos = GetCaretViewPosition();
4132 int nViewLine = ptCaretViewPos.y;
4133 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4134 if (nPrevViewLine != nViewLine) // not on same view line
4136 // find previous suitable screen line
4137 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4139 if (nPrevLine <= 0)
4141 return;
4143 nPrevLine--;
4144 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4147 ptCaretPos.y = nPrevLine;
4148 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4149 SetCaretPosition(ptCaretPos);
4150 OnCaretMove(MOVELEFT);
4151 ShowDiffLines(ptCaretPos.y);
4154 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4156 switch (GetCharGroup(ch))
4158 case CHG_CONTROL:
4159 case CHG_WHITESPACE:
4160 case CHG_WORDSEPARATOR:
4161 return true;
4163 return false;
4166 bool CBaseView::IsCaretAtWordBoundary()
4168 POINT ptViewCaret = GetCaretViewPosition();
4169 CString line = GetViewLineChars(ptViewCaret.y);
4170 if (line.IsEmpty())
4171 return false; // no boundary at the empty lines
4172 if (ptViewCaret.x == 0)
4173 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4174 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4175 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4176 return
4177 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4178 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4181 void CBaseView::UpdateViewsCaretPosition()
4183 POINT ptCaretPos = GetCaretPosition();
4184 if (m_pwndBottom && m_pwndBottom!=this)
4185 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4186 if (m_pwndLeft && m_pwndLeft!=this)
4187 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4188 if (m_pwndRight && m_pwndRight!=this)
4189 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4192 void CBaseView::OnCaretWordleft()
4194 MoveCaretWordLeft();
4195 OnCaretMove(MOVELEFT);
4198 void CBaseView::OnCaretWordright()
4200 MoveCaretWordRight();
4201 OnCaretMove(MOVERIGHT);
4204 void CBaseView::MoveCaretWordLeft()
4206 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4211 void CBaseView::MoveCaretWordRight()
4213 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4218 void CBaseView::ClearCurrentSelection()
4220 m_ptSelectionViewPosStart = GetCaretViewPosition();
4221 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4222 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4223 m_nSelViewBlockStart = -1;
4224 m_nSelViewBlockEnd = -1;
4225 Invalidate(FALSE);
4228 void CBaseView::ClearSelection()
4230 if (m_pwndLeft)
4231 m_pwndLeft->ClearCurrentSelection();
4232 if (m_pwndRight)
4233 m_pwndRight->ClearCurrentSelection();
4234 if (m_pwndBottom)
4235 m_pwndBottom->ClearCurrentSelection();
4238 void CBaseView::AdjustSelection(bool bMoveLeft)
4240 POINT ptCaretViewPos = GetCaretViewPosition();
4241 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4243 // select all have been used recently update origin
4244 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4246 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4247 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4249 m_ptSelectionViewPosStart = ptCaretViewPos;
4250 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4252 else
4254 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4255 m_ptSelectionViewPosEnd = ptCaretViewPos;
4258 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4260 Invalidate(FALSE);
4263 void CBaseView::OnEditCut()
4265 if (IsWritable())
4267 OnEditCopy();
4268 RemoveSelectedText();
4272 void CBaseView::OnEditPaste()
4274 if (IsWritable())
4276 CUndo::GetInstance().BeginGrouping();
4277 RemoveSelectedText();
4278 PasteText();
4279 CUndo::GetInstance().EndGrouping();
4283 void CBaseView::DeleteFonts()
4285 for (int i=0; i<fontsCount; i++)
4287 if (m_apFonts[i] != NULL)
4289 m_apFonts[i]->DeleteObject();
4290 delete m_apFonts[i];
4291 m_apFonts[i] = NULL;
4296 void CBaseView::OnCaretMove(bool bMoveLeft)
4298 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4299 OnCaretMove(bMoveLeft, bShift);
4302 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4304 if(isShiftPressed)
4305 AdjustSelection(bMoveLeft);
4306 else
4307 ClearSelection();
4308 EnsureCaretVisible();
4309 UpdateCaret();
4312 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4314 AddCutCopyAndPaste(popup);
4317 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4319 popup.AppendMenu(MF_SEPARATOR, NULL);
4320 CString temp;
4321 temp.LoadString(IDS_EDIT_COPY);
4322 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4323 if (IsWritable())
4325 temp.LoadString(IDS_EDIT_CUT);
4326 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4327 temp.LoadString(IDS_EDIT_PASTE);
4328 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4329 popup.AppendMenu(MF_SEPARATOR, NULL);
4333 void CBaseView::CompensateForKeyboard(CPoint& point)
4335 // if the context menu is invoked through the keyboard, we have to use
4336 // a calculated position on where to anchor the menu on
4337 if (ArePointsSame(point, SetupPoint(-1, -1)))
4339 CRect rect;
4340 GetWindowRect(&rect);
4341 point = rect.CenterPoint();
4345 HICON CBaseView::LoadIcon(WORD iconId)
4347 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4348 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4349 return (HICON)icon;
4352 void CBaseView::ReleaseBitmap()
4354 if (m_pCacheBitmap != NULL)
4356 m_pCacheBitmap->DeleteObject();
4357 delete m_pCacheBitmap;
4358 m_pCacheBitmap = NULL;
4362 void CBaseView::BuildMarkedWordArray()
4364 int lineCount = GetLineCount();
4365 m_arMarkedWordLines.clear();
4366 m_arMarkedWordLines.reserve(lineCount);
4367 bool bDoit = !m_sMarkedWord.IsEmpty();
4368 for (int i = 0; i < lineCount; ++i)
4370 if (bDoit)
4372 CString line = GetLineChars(i);
4374 if (!line.IsEmpty())
4376 int found = 0;
4377 int nMarkStart = -1;
4378 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4380 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4381 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4382 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4383 if (eLeft != eStart)
4385 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4386 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4387 if (eRight != eEnd)
4389 found = 1;
4390 break;
4394 m_arMarkedWordLines.push_back(found);
4396 else
4397 m_arMarkedWordLines.push_back(0);
4399 else
4400 m_arMarkedWordLines.push_back(0);
4404 void CBaseView::BuildFindStringArray()
4406 int lineCount = GetLineCount();
4407 m_arFindStringLines.clear();
4408 m_arFindStringLines.reserve(lineCount);
4409 bool bDoit = !m_sFindText.IsEmpty();
4410 int s = 0;
4411 int e = 0;
4412 for (int i = 0; i < lineCount; ++i)
4414 if (bDoit)
4416 CString line = GetLineChars(i);
4418 if (!line.IsEmpty())
4420 switch (m_pViewData->GetState(i))
4422 case DIFFSTATE_EMPTY:
4423 m_arFindStringLines.push_back(0);
4424 break;
4425 case DIFFSTATE_UNKNOWN:
4426 case DIFFSTATE_NORMAL:
4427 if (m_bLimitToDiff)
4429 m_arFindStringLines.push_back(0);
4430 break;
4432 case DIFFSTATE_REMOVED:
4433 case DIFFSTATE_REMOVEDWHITESPACE:
4434 case DIFFSTATE_ADDED:
4435 case DIFFSTATE_ADDEDWHITESPACE:
4436 case DIFFSTATE_WHITESPACE:
4437 case DIFFSTATE_WHITESPACE_DIFF:
4438 case DIFFSTATE_CONFLICTED:
4439 case DIFFSTATE_CONFLICTED_IGNORED:
4440 case DIFFSTATE_CONFLICTADDED:
4441 case DIFFSTATE_CONFLICTEMPTY:
4442 case DIFFSTATE_CONFLICTRESOLVED:
4443 case DIFFSTATE_IDENTICALREMOVED:
4444 case DIFFSTATE_IDENTICALADDED:
4445 case DIFFSTATE_THEIRSREMOVED:
4446 case DIFFSTATE_THEIRSADDED:
4447 case DIFFSTATE_YOURSREMOVED:
4448 case DIFFSTATE_YOURSADDED:
4449 case DIFFSTATE_EDITED:
4451 if (!m_bMatchCase)
4452 line = line.MakeLower();
4453 s = 0;
4454 e = 0;
4455 int match = 0;
4456 while (StringFound(line, SearchNext, s, e))
4458 match++;
4459 s = e;
4460 e = 0;
4462 m_arFindStringLines.push_back(match);
4463 break;
4465 default:
4466 m_arFindStringLines.push_back(0);
4469 else
4470 m_arFindStringLines.push_back(0);
4472 else
4473 m_arFindStringLines.push_back(0);
4475 UpdateLocator();
4478 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4480 if (!m_bShowInlineDiff)
4481 return false;
4482 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4483 return false;
4485 CString sLine = GetViewLineChars(nViewLine);
4486 if (sLine.IsEmpty())
4487 return false;
4489 CheckOtherView();
4490 if (!m_pOtherViewData)
4491 return false;
4493 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4494 if (sDiffLine.IsEmpty())
4495 return false;
4497 CString sLineExp = ExpandChars(sLine);
4498 CString sDiffLineExp = ExpandChars(sDiffLine);
4499 svn_diff_t * diff = NULL;
4500 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4501 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4502 return false;
4504 size_t lineoffset = 0;
4505 size_t position = 0;
4506 while (diff)
4508 apr_off_t len = diff->original_length;
4509 size_t oldpos = position;
4511 for (apr_off_t i = 0; i < len; ++i)
4513 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4514 lineoffset++;
4517 if (diff->type == svn_diff__type_diff_modified)
4519 inlineDiffPos p;
4520 p.start = oldpos;
4521 p.end = position;
4522 positions.push_back(p);
4525 diff = diff->next;
4528 return !positions.empty();
4531 void CBaseView::OnNavigateNextinlinediff()
4533 int nX;
4534 if (GetNextInlineDiff(nX))
4536 POINT ptCaretViewPos = GetCaretViewPosition();
4537 ptCaretViewPos.x = nX;
4538 SetCaretAndGoalViewPosition(ptCaretViewPos);
4539 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4540 EnsureCaretVisible();
4544 void CBaseView::OnNavigatePrevinlinediff()
4546 int nX;
4547 if (GetPrevInlineDiff(nX))
4549 POINT ptCaretViewPos = GetCaretViewPosition();
4550 ptCaretViewPos.x = nX;
4551 SetCaretAndGoalViewPosition(ptCaretViewPos);
4552 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4553 EnsureCaretVisible();
4557 bool CBaseView::HasNextInlineDiff()
4559 int nPos;
4560 return GetNextInlineDiff(nPos);
4563 bool CBaseView::GetNextInlineDiff(int & nPos)
4565 POINT ptCaretViewPos = GetCaretViewPosition();
4566 std::vector<inlineDiffPos> positions;
4567 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4569 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4571 if (it->start > ptCaretViewPos.x)
4573 nPos = (LONG)it->start;
4574 return true;
4576 if (it->end > ptCaretViewPos.x)
4578 nPos = (LONG)it->end;
4579 return true;
4583 return false;
4586 bool CBaseView::HasPrevInlineDiff()
4588 int nPos;
4589 return GetPrevInlineDiff(nPos);
4592 bool CBaseView::GetPrevInlineDiff(int & nPos)
4594 POINT ptCaretViewPos = GetCaretViewPosition();
4595 std::vector<inlineDiffPos> positions;
4596 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4598 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4600 if ( it->end < ptCaretViewPos.x)
4602 nPos = (LONG)it->end;
4603 return true;
4605 if ( it->start < ptCaretViewPos.x)
4607 nPos = (LONG)it->start;
4608 return true;
4612 return false;
4615 CBaseView * CBaseView::GetFirstGoodView()
4617 if (IsViewGood(m_pwndLeft))
4618 return m_pwndLeft;
4619 if (IsViewGood(m_pwndRight))
4620 return m_pwndRight;
4621 if (IsViewGood(m_pwndBottom))
4622 return m_pwndBottom;
4623 return NULL;
4626 void CBaseView::BuildAllScreen2ViewVector()
4628 CBaseView * p_pwndView = GetFirstGoodView();
4629 if (p_pwndView)
4631 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4635 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4637 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4640 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4642 CBaseView * p_pwndView = GetFirstGoodView();
4643 if (p_pwndView)
4645 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4649 void CBaseView::UpdateViewLineNumbers()
4651 int nLineNumber = 0;
4652 int nViewLineCount = GetViewCount();
4653 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4655 int oldLine = (int)GetViewLineNumber(nViewLine);
4656 if (oldLine >= 0)
4657 SetViewLineNumber(nViewLine, nLineNumber++);
4659 m_nDigits = 0;
4662 int CBaseView::CleanEmptyLines()
4664 int nRemovedCount = 0;
4665 int nViewLineCount = GetViewCount();
4666 bool bCheckLeft = IsViewGood(m_pwndLeft);
4667 bool bCheckRight = IsViewGood(m_pwndRight);
4668 bool bCheckBottom = IsViewGood(m_pwndBottom);
4669 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4671 bool bAllEmpty = true;
4672 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4673 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4674 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4675 if (bAllEmpty)
4677 if (bCheckLeft)
4679 m_pwndLeft->RemoveViewData(nViewLine);
4681 if (bCheckRight)
4683 m_pwndRight->RemoveViewData(nViewLine);
4685 if (bCheckBottom)
4687 m_pwndBottom->RemoveViewData(nViewLine);
4689 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4691 SaveUndoStep();
4693 nViewLineCount--;
4694 nRemovedCount++;
4695 continue;
4697 nViewLine++;
4699 return nRemovedCount;
4702 int CBaseView::FindScreenLineForViewLine( int viewLine )
4704 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4707 int CBaseView::CountMultiLines( int nViewLine )
4709 if (m_ScreenedViewLine.empty())
4710 return 0; // in case the view is completely empty
4712 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4714 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4716 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4719 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4721 TScreenedViewLine oScreenedLine;
4722 // tokenize string
4723 int prevpos = 0;
4724 int pos = 0;
4725 while ((pos = multiline.Find('\n', pos)) >= 0)
4727 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4728 pos++;
4729 prevpos = pos;
4731 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4732 oScreenedLine.bSublinesSet = true;
4733 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4735 return CountMultiLines(nViewLine);
4738 /// prepare inline diff cache
4739 LineColors & CBaseView::GetLineColors(int nViewLine)
4741 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4743 if (m_bWhitespaceInlineDiffs)
4745 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4746 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4748 else
4750 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4751 return m_ScreenedViewLine[nViewLine].lineColors;
4754 LineColors oLineColors;
4755 // set main line color
4756 COLORREF crBkgnd, crText;
4757 DiffStates diffState = m_pViewData->GetState(nViewLine);
4758 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4759 oLineColors.SetColor(0, crText, crBkgnd);
4761 do {
4762 if (!m_bShowInlineDiff)
4763 break;
4765 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4766 break;
4768 CString sLine = GetViewLineChars(nViewLine);
4769 if (sLine.IsEmpty())
4770 break;
4771 if (!m_pOtherView)
4772 break;
4774 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4775 if (sDiffLine.IsEmpty())
4776 break;
4778 svn_diff_t * diff = NULL;
4779 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4780 break;
4781 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4782 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4783 break;
4785 int lineoffset = 0;
4786 int nTextStartOffset = 0;
4787 std::map<int, COLORREF> removedPositions;
4788 while (diff)
4790 apr_off_t len = diff->original_length;
4792 CString s;
4793 for (int i = 0; i < len; ++i)
4795 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4796 lineoffset++;
4798 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4799 int nTextLength = s.GetLength();
4801 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4802 if ((m_bShowInlineDiff)&&(bInlineDiff))
4804 crBkgnd = InlineViewLineDiffColor(nViewLine);
4806 else
4808 crBkgnd = m_ModifiedBk;
4811 if (len < diff->modified_length)
4813 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4815 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4817 nTextStartOffset += nTextLength;
4818 diff = diff->next;
4820 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4822 oLineColors.AddShotColor(it->first, it->second);
4824 } while (false); // error catch
4826 if (!m_bWhitespaceInlineDiffs)
4828 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4829 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4831 else
4833 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4834 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4837 return GetLineColors(nViewLine);
4840 void CBaseView::OnEditSelectall()
4842 if (m_pViewData == nullptr)
4843 return;
4844 int nLastViewLine = m_pViewData->GetCount()-1;
4845 if (nLastViewLine < 0)
4846 return;
4847 SetupAllViewSelection(0, nLastViewLine);
4849 CString sLine = GetViewLineChars(nLastViewLine);
4850 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4851 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4852 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4854 UpdateWindow();
4857 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4859 FilterWhitespaces(first);
4860 FilterWhitespaces(second);
4863 void CBaseView::FilterWhitespaces(CString& line)
4865 line.Remove(' ');
4866 line.Remove('\t');
4867 line.Remove('\r');
4868 line.Remove('\n');
4871 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4873 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4874 int nEventLine = nLineFromTop + m_nTopLine;
4875 nEventLine--; //we need the index
4876 return nEventLine;
4880 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4882 if (RelayTrippleClick(pMsg))
4883 return TRUE;
4884 return CView::PreTranslateMessage(pMsg);
4888 void CBaseView::ResetUndoStep()
4890 m_AllState.Clear();
4893 void CBaseView::SaveUndoStep()
4895 if (!m_AllState.IsEmpty())
4897 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4899 ResetUndoStep();
4902 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4904 m_pState->addedlines.push_back(index);
4905 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4908 void CBaseView::InsertViewData( int index, const viewdata& data )
4910 m_pState->addedlines.push_back(index);
4911 m_pViewData->InsertData(index, data);
4914 void CBaseView::RemoveViewData( int index )
4916 m_pState->removedlines[index] = m_pViewData->GetData(index);
4917 m_pViewData->RemoveData(index);
4920 void CBaseView::SetViewData( int index, const viewdata& data )
4922 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4923 m_pViewData->SetData(index, data);
4926 void CBaseView::SetViewState( int index, DiffStates state )
4928 m_pState->linestates[index] = m_pViewData->GetState(index);
4929 m_pViewData->SetState(index, state);
4932 void CBaseView::SetViewLine( int index, const CString& sLine )
4934 m_pState->difflines[index] = m_pViewData->GetLine(index);
4935 m_pViewData->SetLine(index, sLine);
4938 void CBaseView::SetViewLineNumber( int index, int linenumber )
4940 int oldLineNumber = m_pViewData->GetLineNumber(index);
4941 if (oldLineNumber != linenumber) {
4942 m_pState->linelines[index] = oldLineNumber;
4943 m_pViewData->SetLineNumber(index, linenumber);
4947 void CBaseView::SetViewLineEnding( int index, EOL ending )
4949 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4950 m_pViewData->SetLineEnding(index, ending);
4953 void CBaseView::SetViewMarked( int index, bool marked )
4955 m_pViewData->SetMarked(index, marked);
4959 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4961 if (HasSelection())
4963 start = m_nSelViewBlockStart;
4964 end = m_nSelViewBlockEnd;
4965 return true;
4967 return false;
4970 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4972 RebuildIfNecessary();
4973 if (size() <= screenLine)
4974 return 0;
4975 return m_Screen2View[screenLine].nViewLine;
4978 int CBaseView::Screen2View::size()
4980 RebuildIfNecessary();
4981 return (int)m_Screen2View.size();
4984 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4986 RebuildIfNecessary();
4987 if (size() <= screenLine)
4988 return 0;
4989 return m_Screen2View[screenLine].nViewSubLine;
4992 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4994 RebuildIfNecessary();
4995 return m_Screen2View[screenLine];
4999 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5001 void CBaseView::Screen2View::RebuildIfNecessary()
5003 if (!m_pViewData)
5004 return; // rebuild not necessary
5006 FixScreenedCacheSize(m_pwndLeft);
5007 FixScreenedCacheSize(m_pwndRight);
5008 FixScreenedCacheSize(m_pwndBottom);
5009 if (!m_bFull)
5011 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
5013 ResetScreenedViewLineCache(m_pwndLeft, *it);
5014 ResetScreenedViewLineCache(m_pwndRight, *it);
5015 ResetScreenedViewLineCache(m_pwndBottom, *it);
5018 else
5020 ResetScreenedViewLineCache(m_pwndLeft);
5021 ResetScreenedViewLineCache(m_pwndRight);
5022 ResetScreenedViewLineCache(m_pwndBottom);
5024 m_RebuildRanges.clear();
5025 m_bFull = false;
5027 size_t OldSize = m_Screen2View.size();
5028 m_Screen2View.clear();
5029 m_Screen2View.reserve(OldSize); // guess same size
5030 for (int i = 0; i < m_pViewData->GetCount(); ++i)
5032 if (m_pMainFrame->m_bCollapsed)
5034 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
5035 ++i;
5036 if (!(i < m_pViewData->GetCount()))
5037 break;
5039 TScreenLineInfo oLineInfo;
5040 oLineInfo.nViewLine = i;
5041 oLineInfo.nViewSubLine = -1; // no wrap
5042 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
5044 int nMaxLines = 0;
5045 if (IsLeftViewGood())
5046 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
5047 if (IsRightViewGood())
5048 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
5049 if (IsBottomViewGood())
5050 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
5051 for (int l = 0; l < (nMaxLines-1); ++l)
5053 oLineInfo.nViewSubLine++;
5054 m_Screen2View.push_back(oLineInfo);
5056 oLineInfo.nViewSubLine++;
5058 m_Screen2View.push_back(oLineInfo);
5060 m_pViewData = NULL;
5062 if (IsLeftViewGood())
5063 m_pwndLeft->BuildMarkedWordArray();
5064 if (IsRightViewGood())
5065 m_pwndRight->BuildMarkedWordArray();
5066 if (IsBottomViewGood())
5067 m_pwndBottom->BuildMarkedWordArray();
5068 UpdateLocator();
5069 RecalcAllVertScrollBars();
5070 RecalcAllHorzScrollBars();
5073 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5075 RebuildIfNecessary();
5077 int nScreenLineCount = (int)m_Screen2View.size();
5079 int nPos = 0;
5080 if (nScreenLineCount>16)
5082 // for enough long data search for last screen
5083 // with viewline less than one we are looking for
5084 // use approximate method (based on) binary search using asymmetric start point
5085 // in form 2**n (determined as MSB of length) to go around division and rounding;
5086 // this effectively looks for bit values from MSB to LSB
5088 int nTestBit;
5089 //GetMostSignificantBitValue
5090 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5091 nTestBit = nScreenLineCount;
5092 nTestBit |= nTestBit>>1;
5093 nTestBit |= nTestBit>>2;
5094 nTestBit |= nTestBit>>4;
5095 nTestBit |= nTestBit>>8;
5096 nTestBit |= nTestBit>>16;
5097 nTestBit ^= (nTestBit>>1);
5099 while (nTestBit)
5101 int nTestPos = nPos | nTestBit;
5102 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5104 nPos = nTestPos;
5106 nTestBit >>= 1;
5109 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5111 nPos++;
5114 return nPos;
5117 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5118 m_bFull = true;
5120 m_pViewData = pViewData;
5123 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5125 if (m_bFull)
5126 return;
5128 m_pViewData = pViewData;
5130 TRebuildRange Range;
5131 Range.FirstViewLine=nFirstViewLine;
5132 Range.LastViewLine=nLastViewLine;
5133 m_RebuildRanges.push_back(Range);
5136 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5138 if (!IsViewGood(pwndView))
5140 return false;
5142 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5143 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5144 if (nOldSize == nViewCount)
5146 return false;
5148 pwndView->m_ScreenedViewLine.resize(nViewCount);
5149 return true;
5152 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5154 if (!IsViewGood(pwndView))
5156 return false;
5158 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5159 ResetScreenedViewLineCache(pwndView, Range);
5160 return true;
5163 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5165 if (!IsViewGood(pwndView))
5167 return false;
5169 if (Range.LastViewLine == -1)
5171 return false;
5173 ASSERT(Range.FirstViewLine >= 0);
5174 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5175 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5177 pwndView->m_ScreenedViewLine[i].Clear();
5179 return false;
5182 void CBaseView::WrapChanged()
5184 m_nMaxLineLength = -1;
5185 m_nOffsetChar = 0;
5188 void CBaseView::OnEditFind()
5190 if (m_pFindDialog)
5191 return;
5193 m_pFindDialog = new CFindDlg(this);
5194 m_pFindDialog->Create(this);
5196 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5199 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5201 ASSERT(m_pFindDialog != NULL);
5203 if (m_pFindDialog->IsTerminating())
5205 // invalidate the handle identifying the dialog box.
5206 m_pFindDialog = NULL;
5207 return 0;
5210 if(m_pFindDialog->FindNext())
5212 //read data from dialog
5213 m_sFindText = m_pFindDialog->GetFindString();
5214 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5215 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5216 m_bWholeWord = m_pFindDialog->WholeWord();
5218 if (!m_bMatchCase)
5219 m_sFindText = m_sFindText.MakeLower();
5221 BuildFindStringArray();
5222 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5224 if (m_pFindDialog->SearchUp())
5225 OnEditFindprev();
5226 else
5227 OnEditFindnext();
5229 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5231 size_t count = 0;
5232 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5233 count += m_arFindStringLines[i];
5234 CString format;
5235 format.LoadString(IDS_FIND_COUNT);
5236 CString matches;
5237 matches.Format(format, count);
5238 m_pFindDialog->SetStatusText(matches);
5242 return 0;
5245 void CBaseView::OnEditFindnextStart()
5247 if (m_pViewData == nullptr)
5248 return;
5249 if (HasTextSelection())
5251 m_sFindText = GetSelectedText();
5252 m_bMatchCase = false;
5253 m_bLimitToDiff = false;
5254 m_bWholeWord = false;
5255 m_sFindText = m_sFindText.MakeLower();
5257 BuildFindStringArray();
5258 OnEditFindnext();
5260 else
5262 m_sFindText.Empty();
5263 BuildFindStringArray();
5267 void CBaseView::OnEditFindprevStart()
5269 if (m_pViewData == nullptr)
5270 return;
5271 if (HasTextSelection())
5273 m_sFindText = GetSelectedText();
5274 m_bMatchCase = false;
5275 m_bLimitToDiff = false;
5276 m_bWholeWord = false;
5277 m_sFindText = m_sFindText.MakeLower();
5279 BuildFindStringArray();
5280 OnEditFindprev();
5282 else
5284 m_sFindText.Empty();
5285 BuildFindStringArray();
5289 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5291 if (srchDir == SearchPrevious)
5293 int laststart = -1;
5294 int laststart2 = -1;
5297 laststart2 = laststart;
5298 laststart = str.Find(m_sFindText, laststart + 1);
5299 } while (laststart >= 0 && laststart < start);
5300 start = laststart2;
5302 else
5303 start = str.Find(m_sFindText, start);
5304 end = start + m_sFindText.GetLength();
5305 bool bStringFound = (start >= 0);
5306 if (bStringFound && m_bWholeWord)
5308 if (start)
5309 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5311 if (bStringFound)
5313 if (str.GetLength() > end)
5314 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5317 return bStringFound;
5320 void CBaseView::OnEditFindprev()
5322 Search(SearchPrevious);
5325 void CBaseView::OnEditFindnext()
5327 Search(SearchNext);
5330 void CBaseView::Search(SearchDirection srchDir)
5332 if (m_sFindText.IsEmpty())
5333 return;
5334 if(!m_pViewData)
5335 return;
5337 POINT start = m_ptSelectionViewPosEnd;
5338 POINT end;
5339 end.y = m_pViewData->GetCount()-1;
5340 if (end.y < 0)
5341 return;
5343 if (srchDir==SearchNext)
5344 end.x = GetViewLineLength(end.y);
5345 else
5347 end.x = m_ptSelectionViewPosStart.x;
5348 start.x = 0;
5351 if (!HasTextSelection())
5353 start.y = m_ptCaretViewPos.y;
5354 if (srchDir==SearchNext)
5355 start.x = m_ptCaretViewPos.x;
5356 else
5358 start.x = 0;
5359 end.x = m_ptCaretViewPos.x;
5362 CString sSelectedText;
5363 int startline = -1;
5364 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5366 if (nViewLine < 0)
5368 nViewLine = m_pViewData->GetCount()-1;
5369 startline = start.y;
5370 if (m_pFindDialog)
5371 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5372 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5374 if (nViewLine > end.y)
5376 nViewLine = 0;
5377 startline = start.y;
5378 if (m_pFindDialog)
5379 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5380 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5382 switch (m_pViewData->GetState(nViewLine))
5384 case DIFFSTATE_EMPTY:
5385 break;
5386 case DIFFSTATE_UNKNOWN:
5387 case DIFFSTATE_NORMAL:
5388 if (m_bLimitToDiff)
5389 break;
5390 case DIFFSTATE_REMOVED:
5391 case DIFFSTATE_REMOVEDWHITESPACE:
5392 case DIFFSTATE_ADDED:
5393 case DIFFSTATE_ADDEDWHITESPACE:
5394 case DIFFSTATE_WHITESPACE:
5395 case DIFFSTATE_WHITESPACE_DIFF:
5396 case DIFFSTATE_CONFLICTED:
5397 case DIFFSTATE_CONFLICTED_IGNORED:
5398 case DIFFSTATE_CONFLICTADDED:
5399 case DIFFSTATE_CONFLICTEMPTY:
5400 case DIFFSTATE_CONFLICTRESOLVED:
5401 case DIFFSTATE_IDENTICALREMOVED:
5402 case DIFFSTATE_IDENTICALADDED:
5403 case DIFFSTATE_THEIRSREMOVED:
5404 case DIFFSTATE_THEIRSADDED:
5405 case DIFFSTATE_YOURSREMOVED:
5406 case DIFFSTATE_YOURSADDED:
5407 case DIFFSTATE_EDITED:
5409 sSelectedText = GetViewLineChars(nViewLine);
5410 if (nViewLine == start.y && startline < 0)
5411 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5412 if (!m_bMatchCase)
5413 sSelectedText = sSelectedText.MakeLower();
5414 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5415 int endfound = 0;
5416 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5418 HighlightViewLines(nViewLine, nViewLine);
5419 m_ptSelectionViewPosStart.x = startfound;
5420 m_ptSelectionViewPosEnd.x = endfound;
5421 if (nViewLine == start.y && startline < 0)
5423 m_ptSelectionViewPosStart.x += start.x;
5424 m_ptSelectionViewPosEnd.x += start.x;
5426 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5427 m_ptSelectionViewPosStart.y = nViewLine;
5428 m_ptSelectionViewPosEnd.y = nViewLine;
5429 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5430 UpdateViewsCaretPosition();
5431 EnsureCaretVisible();
5432 Invalidate();
5433 return;
5436 break;
5439 if (startline >= 0)
5441 if (nViewLine == startline)
5443 CString message;
5444 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5445 if (m_pFindDialog)
5446 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5447 ::MessageBeep(0xFFFFFFFF);
5448 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5449 break;
5453 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5456 CString CBaseView::GetSelectedText() const
5458 CString sSelectedText;
5459 POINT start = m_ptSelectionViewPosStart;
5460 POINT end = m_ptSelectionViewPosEnd;
5461 if (!HasTextSelection())
5463 if (!HasSelection())
5464 return sSelectedText;
5465 start.y = m_nSelViewBlockStart;
5466 start.x = 0;
5467 end.y = m_nSelViewBlockEnd;
5468 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5470 if (m_pViewData == nullptr)
5471 return sSelectedText;
5472 // first store the selected lines in one CString
5473 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5475 switch (m_pViewData->GetState(nViewLine))
5477 case DIFFSTATE_EMPTY:
5478 break;
5479 case DIFFSTATE_UNKNOWN:
5480 case DIFFSTATE_NORMAL:
5481 case DIFFSTATE_REMOVED:
5482 case DIFFSTATE_REMOVEDWHITESPACE:
5483 case DIFFSTATE_ADDED:
5484 case DIFFSTATE_ADDEDWHITESPACE:
5485 case DIFFSTATE_WHITESPACE:
5486 case DIFFSTATE_WHITESPACE_DIFF:
5487 case DIFFSTATE_CONFLICTED:
5488 case DIFFSTATE_CONFLICTED_IGNORED:
5489 case DIFFSTATE_CONFLICTADDED:
5490 case DIFFSTATE_CONFLICTEMPTY:
5491 case DIFFSTATE_CONFLICTRESOLVED:
5492 case DIFFSTATE_IDENTICALREMOVED:
5493 case DIFFSTATE_IDENTICALADDED:
5494 case DIFFSTATE_THEIRSREMOVED:
5495 case DIFFSTATE_THEIRSADDED:
5496 case DIFFSTATE_YOURSREMOVED:
5497 case DIFFSTATE_YOURSADDED:
5498 case DIFFSTATE_EDITED:
5499 sSelectedText += GetViewLineChars(nViewLine);
5500 sSelectedText += _T("\r\n");
5501 break;
5504 // remove the non-selected chars from the first line, last line and last \r\n
5505 int nLeftCut = start.x;
5506 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5507 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5508 return sSelectedText;
5511 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5513 hasMods = false;
5514 hasConflicts = false;
5515 hasWhitespaceMods = false;
5517 if (m_pViewData)
5519 for (int i=0; i<m_pViewData->GetCount(); i++)
5521 DiffStates state = m_pViewData->GetState(i);
5522 switch (state)
5524 case DIFFSTATE_ADDED:
5525 case DIFFSTATE_IDENTICALADDED:
5526 case DIFFSTATE_THEIRSADDED:
5527 case DIFFSTATE_YOURSADDED:
5528 case DIFFSTATE_CONFLICTADDED:
5529 case DIFFSTATE_IDENTICALREMOVED:
5530 case DIFFSTATE_REMOVED:
5531 case DIFFSTATE_THEIRSREMOVED:
5532 case DIFFSTATE_YOURSREMOVED:
5533 case DIFFSTATE_EMPTY:
5534 hasMods = true;
5535 break;
5536 case DIFFSTATE_CONFLICTED:
5537 case DIFFSTATE_CONFLICTED_IGNORED:
5538 hasConflicts = true;
5539 break;
5540 case DIFFSTATE_REMOVEDWHITESPACE:
5541 case DIFFSTATE_ADDEDWHITESPACE:
5542 case DIFFSTATE_WHITESPACE:
5543 case DIFFSTATE_WHITESPACE_DIFF:
5544 hasWhitespaceMods = true;
5545 break;
5551 void CBaseView::OnEditGotoline()
5553 if (m_pViewData == NULL)
5554 return;
5555 // find the last and first line number
5556 int nViewLineCount = m_pViewData->GetCount();
5558 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5559 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5561 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5562 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5564 break;
5567 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5569 return;
5571 nLastLineNumber++;
5572 int nFirstLineNumber=1; // first is always 1
5574 CString sText;
5575 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5577 CGotoLineDlg dlg(this);
5578 dlg.SetLabel(sText);
5579 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5580 if (dlg.DoModal() == IDOK)
5582 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5584 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5586 HighlightViewLines(nViewLine, nViewLine);
5587 return;
5593 void CBaseView::OnToggleReadonly()
5595 if (IsReadonlyChangable()) {
5596 SetWritable(IsReadonly());
5600 int CBaseView::SaveFile(int nFlags)
5602 Invalidate();
5603 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5605 CFileTextLines file;
5606 m_SaveParams.m_LineEndings = m_lineendings;
5607 m_SaveParams.m_UnicodeType = m_texttype;
5608 file.SetSaveParams(m_SaveParams);
5610 for (int i=0; i<m_pViewData->GetCount(); i++)
5612 //only copy non-removed lines
5613 DiffStates state = m_pViewData->GetState(i);
5614 switch (state)
5616 case DIFFSTATE_CONFLICTED:
5617 case DIFFSTATE_CONFLICTED_IGNORED:
5619 int first = i;
5620 int last = i;
5623 last++;
5624 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5625 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5626 for (int j=first; j<last; j++)
5628 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5630 file.Add(_T("======="), EOL_NOENDING);
5631 for (int j=first; j<last; j++)
5633 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5635 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5636 i = last-1;
5638 break;
5639 case DIFFSTATE_EMPTY:
5640 break;
5641 case DIFFSTATE_CONFLICTEMPTY:
5642 case DIFFSTATE_IDENTICALREMOVED:
5643 case DIFFSTATE_REMOVED:
5644 case DIFFSTATE_THEIRSREMOVED:
5645 case DIFFSTATE_YOURSREMOVED:
5646 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5647 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5649 // do not save removed lines
5650 break;
5652 default:
5653 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5654 break;
5657 CString filename = m_pWorkingFile->GetFilename();
5658 if (m_pWorkingFile->IsReadonly())
5659 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5660 return -1;
5661 if (!file.Save(filename))
5663 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5664 return -1;
5666 m_pWorkingFile->SetFileName(filename);
5667 m_pWorkingFile->StoreFileAttributes();
5668 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5669 SetModified(FALSE);
5670 CUndo::GetInstance().MarkAsOriginalState(
5671 this == m_pwndLeft,
5672 this == m_pwndRight,
5673 this == m_pwndBottom);
5674 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5675 return 0;
5676 return file.GetCount();
5678 return 1;
5682 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5684 if (m_pWorkingFile)
5686 m_pWorkingFile->SetFileName(sFileName);
5687 return SaveFile(nFlags);
5689 return -1;
5693 EOL CBaseView::GetLineEndings()
5695 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5698 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5700 if (bHasMixedEols)
5702 return EOL_AUTOLINE; // mixed eols - hack value
5704 if (m_lineendings == EOL_AUTOLINE)
5706 return EOL_CRLF;
5708 return m_lineendings;
5711 void CBaseView::ReplaceLineEndings(EOL eEol)
5713 if (eEol == EOL_AUTOLINE)
5715 return;
5717 // set AUTOLINE
5718 m_lineendings = eEol;
5719 // replace all set EOLs
5720 // TODO store line endings and lineendings in undo
5721 //CUndo::BeginGrouping();
5722 for (int i = 0; i < GetViewCount(); ++i)
5724 if (IsLineEmpty(i))
5726 continue;
5728 EOL eLineEol = GetViewLineEnding(i);
5729 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5731 continue;
5733 SetViewLineEnding(i, eEol);
5735 //CUndo::EndGrouping();
5736 //CUndo::saveundostep;
5737 DocumentUpdated();
5738 SetModified();
5741 void CBaseView::SetLineEndingStyle(EOL eEol)
5743 m_lineendings = eEol;
5746 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5748 if (m_texttype == eTextType)
5750 return;
5752 m_texttype = eTextType;
5753 DocumentUpdated();
5754 SetModified();
5757 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5759 if (IsReadonly())
5760 return; // nothing to be changed in read-only view
5761 CEncodingDlg dlg;
5762 dlg.view = CString(MAKEINTRESOURCE(nTextId));
5763 dlg.texttype = m_texttype;
5764 dlg.lineendings = GetLineEndings();
5765 if (dlg.DoModal() != IDOK)
5766 return;
5767 SetTextType(dlg.texttype);
5768 ReplaceLineEndings(dlg.lineendings);
5772 Replaces lines from source view to this
5774 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, bool skipMarked)
5776 if (!IsViewGood(pwndView))
5777 return;
5778 if (!IsWritable())
5779 return;
5780 CUndo::GetInstance().BeginGrouping();
5782 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5784 if (skipMarked && GetViewMarked(viewLine))
5785 continue;
5786 viewdata line = pwndView->GetViewData(viewLine);
5787 if (line.ending != EOL_NOENDING)
5788 line.ending = m_lineendings;
5789 switch (line.state)
5791 case DIFFSTATE_CONFLICTEMPTY:
5792 case DIFFSTATE_UNKNOWN:
5793 line.state = DIFFSTATE_EMPTY;
5794 case DIFFSTATE_EMPTY:
5795 break;
5796 case DIFFSTATE_ADDED:
5797 case DIFFSTATE_CONFLICTADDED:
5798 case DIFFSTATE_CONFLICTED:
5799 case DIFFSTATE_CONFLICTED_IGNORED:
5800 case DIFFSTATE_IDENTICALADDED:
5801 case DIFFSTATE_THEIRSADDED:
5802 case DIFFSTATE_YOURSADDED:
5803 case DIFFSTATE_IDENTICALREMOVED:
5804 case DIFFSTATE_REMOVED:
5805 case DIFFSTATE_THEIRSREMOVED:
5806 case DIFFSTATE_YOURSREMOVED:
5807 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5808 line.state = DIFFSTATE_NORMAL;
5809 case DIFFSTATE_NORMAL:
5810 break;
5811 default:
5812 break;
5814 SetViewData(viewLine, line);
5815 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5817 // if this view is in ASCII and the other is not, we have to make sure that
5818 // the text we copy from the other view can actually be saved in ASCII encoding.
5819 // if not, we have to change this views encoding to the same encoding as the other view
5820 BOOL useDefault = FALSE;
5821 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5822 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5823 SetTextType(pwndView->GetTextType());
5826 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5827 // TODO: check if copied line is same as original one set modified only when differ
5828 SetModified();
5829 SaveUndoStep();
5831 int nRemovedLines = CleanEmptyLines();
5832 SaveUndoStep();
5833 //VerifyEols();
5834 // make sure all non empty line have EOL set but last
5835 // wrong can be last copied line(have eol, but no line under),
5836 // or old last line (line before copied block missing eol, but have line under)
5837 // we'll check all lines to be sure
5838 int nLine = GetViewCount();
5839 // check last line have no EOL set
5840 while (--nLine>=0)
5842 if (!IsViewLineEmpty(nLine))
5844 if (GetViewLineEnding(nLine) != EOL_NOENDING)
5846 // we added non last line into empty block on the end (or should we remove eol from this one ?)
5847 // so next line is empty
5848 ASSERT(IsViewLineEmpty(nLine+1));
5849 // and we can turn it to normal empty line
5850 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
5852 break;
5855 // check all (nonlast) line have EOL set
5856 while (--nLine>=0)
5858 if (!IsViewLineEmpty(nLine))
5860 if (GetViewLineEnding(nLine) == EOL_NOENDING)
5862 SetViewLineEnding(nLine, m_lineendings);
5863 // in theory there should be only one line needing fix, but most of time we get over all anyway
5864 // break;
5868 SaveUndoStep();
5869 UpdateViewLineNumbers();
5870 SaveUndoStep();
5872 CUndo::GetInstance().EndGrouping();
5874 if (nRemovedLines!=0)
5876 // some lines are gone update selection
5877 ClearSelection();
5878 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
5880 BuildAllScreen2ViewVector();
5881 pwndView->Invalidate();
5882 RefreshViews();
5885 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
5887 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5888 SetViewMarked(viewLine, marked);
5889 BuildAllScreen2ViewVector();
5890 Invalidate();
5891 RefreshViews();
5894 void CBaseView::UseViewFileExceptMarked(CBaseView *pwndView)
5896 UseViewBlock(pwndView, 0, GetViewCount() - 1, true);
5899 int CBaseView::GetIndentCharsForLine(int x, int y)
5901 const int maxGuessLine = 100;
5902 int nTabMode = -1;
5903 CString line = GetViewLine(y);
5904 if (m_nTabMode & TABMODE_SMARTINDENT)
5906 // detect left char and right char
5907 TCHAR lc = x > 0 ? line[x - 1] : '\0';
5908 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
5909 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
5910 nTabMode = 1;
5911 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
5912 nTabMode = 0;
5913 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
5914 nTabMode = m_nTabMode & TABMODE_USESPACES;
5916 // detect lines nearby
5917 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
5919 bool above = i > 0 && i >= y - maxGuessLine;
5920 bool below = j < GetViewCount() && j <= y + maxGuessLine;
5921 if (!(above || below))
5922 break;
5923 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
5924 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
5925 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
5926 nTabMode = 1;
5927 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
5928 nTabMode = 0;
5929 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
5930 nTabMode = m_nTabMode & TABMODE_USESPACES;
5933 else
5934 nTabMode = m_nTabMode & TABMODE_USESPACES;
5936 if (nTabMode > 0)
5938 // use spaces
5939 x = CountExpandedChars(line, x);
5940 return (m_nTabSize - (x % m_nTabSize));
5943 // use tab
5944 return 0;
5947 void CBaseView::AddIndentationForSelectedBlock()
5949 bool bModified = false;
5950 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
5952 // skip the line if no character is selected in the last selected line
5953 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
5955 continue;
5957 // skip empty lines
5958 if (IsLineEmpty(nViewLine))
5960 continue;
5962 const CString &sLine = GetViewLine(nViewLine);
5963 CString sTemp = sLine;
5964 if (sTemp.Trim().IsEmpty())
5966 // skip empty and whitechar only lines
5967 continue;
5969 // add tab to line start (alternatively m_nTabSize spaces can be used)
5970 CString tabStr;
5971 int indentChars = GetIndentCharsForLine(0, nViewLine);
5972 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
5973 SetViewLine(nViewLine, tabStr + sLine);
5974 bModified = true;
5976 if (bModified)
5978 SetModified();
5979 SaveUndoStep();
5980 BuildAllScreen2ViewVector();
5984 void CBaseView::RemoveIndentationForSelectedBlock()
5986 bool bModified = false;
5987 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
5989 // skip the line if no character is selected in the last selected line
5990 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
5992 continue;
5994 // skip empty lines
5995 if (IsLineEmpty(nViewLine))
5997 continue;
5999 CString sLine = GetViewLine(nViewLine);
6000 // remove up to n spaces from line start
6001 // and one tab (if less then n spaces was removed)
6002 int nPos = 0;
6003 while (nPos<m_nTabSize)
6005 switch (sLine[nPos])
6007 case ' ':
6008 nPos++;
6009 continue;
6010 case '\t':
6011 nPos++;
6013 break;
6015 if (nPos>0)
6017 sLine.Delete(0, nPos);
6018 SetViewLine(nViewLine, sLine);
6019 bModified = true;
6022 if (bModified)
6024 SetModified();
6025 SaveUndoStep();
6026 BuildAllScreen2ViewVector();
6031 there are two possible versions
6032 - convert tabs to spaces only in front of text (implemented)
6033 - convert all tabs to spaces
6035 void CBaseView::ConvertTabToSpaces()
6037 bool bModified = false;
6038 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6040 if (IsLineEmpty(nViewLine))
6042 continue;
6044 const CString &sLine = GetViewLine(nViewLine);
6045 bool bTabToConvertFound = false;
6046 int nPosIn = 0;
6047 int nPosOut = 0;
6048 while (nPosIn<sLine.GetLength())
6050 switch (sLine[nPosIn])
6052 case ' ':
6053 nPosIn++;
6054 nPosOut++;
6055 continue;
6056 case '\t':
6057 nPosIn++;
6058 bTabToConvertFound = true;
6059 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6060 continue;
6062 break;
6064 if (bTabToConvertFound)
6066 CString sLineNew = sLine;
6067 sLineNew.Delete(0, nPosIn);
6068 sLineNew = CString(' ', nPosOut) + sLineNew;
6069 SetViewLine(nViewLine, sLineNew);
6070 bModified = true;
6073 if (bModified)
6075 SetModified();
6076 SaveUndoStep();
6077 BuildAllScreen2ViewVector();
6082 there are two possible version
6083 - convert spaces to tabs only in front of text (implemented)
6084 - convert all spaces to tabs
6086 void CBaseView::Tabularize()
6088 bool bModified = false;
6089 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6091 if (IsLineEmpty(nViewLine))
6093 continue;
6095 const CString &sLine = GetViewLine(nViewLine);
6096 int nDel = 0;
6097 int nTabCount = 0; // total tabs to be used
6098 int nSpaceCount = 0; // number of spaces in tab size run
6099 int nPos = 0;
6100 while (nPos<sLine.GetLength())
6102 switch (sLine[nPos++])
6104 case ' ':
6105 //bSpace = true;
6106 if (++nSpaceCount < m_nTabSize)
6108 continue;
6110 case '\t':
6111 nTabCount++;
6112 nSpaceCount = 0;
6113 nDel = nPos;
6114 continue;
6116 break;
6118 if (nDel > 0)
6120 CString sLineNew = sLine;
6121 sLineNew.Delete(0, nDel);
6122 sLineNew = CString('\t', nTabCount) + sLineNew;
6123 if (sLine!=sLineNew)
6125 SetViewLine(nViewLine, sLineNew);
6126 bModified = true;
6130 if (bModified)
6132 SetModified();
6133 SaveUndoStep();
6134 BuildAllScreen2ViewVector();
6138 void CBaseView::RemoveTrailWhiteChars()
6140 bool bModified = false;
6141 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6143 if (IsLineEmpty(nViewLine))
6145 continue;
6147 const CString &sLine = GetViewLine(nViewLine);
6148 CString sLineNew = sLine;
6149 sLineNew.TrimRight();
6150 if (sLine.GetLength()!=sLineNew.GetLength())
6152 SetViewLine(nViewLine, sLineNew);
6153 bModified = true;
6156 if (bModified)
6158 SetModified();
6159 SaveUndoStep();
6160 BuildAllScreen2ViewVector();
6164 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6166 if (GetViewCount()>10000)
6168 // 10k lines is enough to check
6169 TWhitecharsProperties oRet = {true, true, true, true};
6170 return oRet;
6172 TWhitecharsProperties oRet = {};
6173 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6175 if (IsLineEmpty(nViewLine))
6177 continue;
6179 const CString &sLine = GetViewLine(nViewLine);
6180 if (sLine.IsEmpty())
6182 continue;
6184 // check leading whites for convertible tabs and spaces
6185 int nPos = 0;
6186 int nSpaceCount = 0; // number of spaces in tab size run
6187 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6189 switch (sLine[nPos++])
6191 case ' ':
6192 if (++nSpaceCount >= m_nTabSize)
6194 oRet.HasSpacesToConvert = true;
6196 continue;
6197 case '\t':
6198 oRet.HasTabsToConvert = true;
6199 if (nSpaceCount!=0)
6201 oRet.HasSpacesToConvert = true;
6203 continue;
6205 break;
6208 // check trailing whites for removable chars
6209 switch (sLine[sLine.GetLength()-1])
6211 case ' ':
6212 case '\t':
6213 oRet.HasTrailWhiteChars = true;
6216 // check EOLs
6217 EOL eLineEol = GetViewLineEnding(nViewLine);
6218 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6220 oRet.HasMixedEols = true;
6223 return oRet;