Add a "replace" and a "replace all" button to the TMerge find dialog
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob85c10f0d83721193f835dff750b6d315507858b7
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2014 - TortoiseSVN
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
28 #include "AppUtils.h"
29 #include "GotoLineDlg.h"
30 #include "EncodingDlg.h"
32 // Note about lines:
33 // We use three different kind of lines here:
34 // 1. The real lines of the original files.
35 // These are shown in the view margin and are not used elsewhere, they're only for user information.
36 // 2. Screen lines.
37 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
38 // 3. View lines.
39 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
41 // Basically view lines are the line data, while screen lines are shown lines.
44 #ifdef _DEBUG
45 #define new DEBUG_NEW
46 #endif
48 #define MARGINWIDTH 20
49 #define HEADERHEIGHT 10
51 #define IDT_SCROLLTIMER 101
53 CBaseView * CBaseView::m_pwndLeft = NULL;
54 CBaseView * CBaseView::m_pwndRight = NULL;
55 CBaseView * CBaseView::m_pwndBottom = NULL;
56 CLocatorBar * CBaseView::m_pwndLocator = NULL;
57 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
58 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
59 CMFCRibbonStatusBar * CBaseView::m_pwndRibbonStatusBar = NULL;
60 CMainFrame * CBaseView::m_pMainFrame = NULL;
61 CBaseView::Screen2View CBaseView::m_Screen2View;
62 const UINT CBaseView::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
64 allviewstate CBaseView::m_AllState;
66 IMPLEMENT_DYNCREATE(CBaseView, CView)
68 CBaseView::CBaseView()
69 : m_pCacheBitmap(NULL)
70 , m_pViewData(NULL)
71 , m_pOtherViewData(NULL)
72 , m_pOtherView(NULL)
73 , m_nLineHeight(-1)
74 , m_nCharWidth(-1)
75 , m_nScreenChars(-1)
76 , m_nLastScreenChars(-1)
77 , m_nMaxLineLength(-1)
78 , m_nScreenLines(-1)
79 , m_nTopLine(0)
80 , m_nOffsetChar(0)
81 , m_nDigits(0)
82 , m_nMouseLine(-1)
83 , m_mouseInMargin(false)
84 , m_bIsHidden(FALSE)
85 , m_lineendings(EOL_AUTOLINE)
86 , m_bReadonly(true)
87 , m_bReadonlyIsChangable(false)
88 , m_bTarget(false)
89 , m_nCaretGoalPos(0)
90 , m_nSelViewBlockStart(-1)
91 , m_nSelViewBlockEnd(-1)
92 , m_bFocused(FALSE)
93 , m_bShowSelection(true)
94 , m_texttype(CFileTextLines::AUTOTYPE)
95 , m_bModified(FALSE)
96 , m_bOtherDiffChecked(false)
97 , m_bInlineWordDiff(true)
98 , m_bWhitespaceInlineDiffs(false)
99 , m_pState(NULL)
100 , m_pFindDialog(NULL)
101 , m_nStatusBarID(0)
102 , m_bMatchCase(false)
103 , m_bLimitToDiff(true)
104 , m_bWholeWord(false)
105 , m_pDC(NULL)
106 , m_pWorkingFile(NULL)
107 , m_bInsertMode(true)
109 m_ptCaretViewPos.x = 0;
110 m_ptCaretViewPos.y = 0;
111 m_ptSelectionViewPosStart = m_ptCaretViewPos;
112 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
113 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
114 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
115 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
116 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
117 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
118 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
119 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
120 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
121 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
122 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?"));
123 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
124 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
125 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
126 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
127 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
128 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
129 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
130 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
131 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
132 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
133 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
134 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
135 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
136 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
137 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
138 m_hMarkedIcon = LoadIcon(IDI_LINEMARKED);
139 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
141 for (int i=0; i<1024; ++i)
142 m_sConflictedText += _T("??");
143 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
145 m_szTip[0] = 0;
146 m_wszTip[0] = 0;
147 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
148 EnableToolTips();
150 m_Eols[EOL_LF] = L"\n"; // x0a
151 m_Eols[EOL_CR] = L"\r"; // x0d
152 m_Eols[EOL_CRLF] = L"\r\n"; // x0d x0a
153 m_Eols[EOL_LFCR] = L"\n\r";
154 m_Eols[EOL_VT] = L"\v"; // x0b
155 m_Eols[EOL_FF] = L"\f"; // x0c
156 m_Eols[EOL_NEL] = L"\x85";
157 m_Eols[EOL_LS] = L"\x2028";
158 m_Eols[EOL_PS] = L"\x2029";
159 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
160 ? EOL_CRLF
161 : m_lineendings];
162 m_SaveParams.m_LineEndings = EOL::EOL_AUTOLINE;
163 m_SaveParams.m_UnicodeType = CFileTextLines::AUTOTYPE;
166 CBaseView::~CBaseView()
168 ReleaseBitmap();
169 DeleteFonts();
170 DestroyIcon(m_hAddedIcon);
171 DestroyIcon(m_hRemovedIcon);
172 DestroyIcon(m_hConflictedIcon);
173 DestroyIcon(m_hConflictedIgnoredIcon);
174 DestroyIcon(m_hWhitespaceBlockIcon);
175 DestroyIcon(m_hEqualIcon);
176 DestroyIcon(m_hLineEndingCR);
177 DestroyIcon(m_hLineEndingCRLF);
178 DestroyIcon(m_hLineEndingLF);
179 DestroyIcon(m_hEditedIcon);
180 DestroyIcon(m_hMovedIcon);
181 DestroyIcon(m_hMarkedIcon);
182 DestroyCursor(m_margincursor);
185 BEGIN_MESSAGE_MAP(CBaseView, CView)
186 ON_WM_VSCROLL()
187 ON_WM_HSCROLL()
188 ON_WM_ERASEBKGND()
189 ON_WM_CREATE()
190 ON_WM_DESTROY()
191 ON_WM_SIZE()
192 ON_WM_MOUSEWHEEL()
193 ON_WM_MOUSEHWHEEL()
194 ON_WM_SETCURSOR()
195 ON_WM_KILLFOCUS()
196 ON_WM_SETFOCUS()
197 ON_WM_CONTEXTMENU()
198 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
199 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
200 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
201 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
202 ON_WM_KEYDOWN()
203 ON_WM_LBUTTONDOWN()
204 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
205 ON_WM_MOUSEMOVE()
206 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
207 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
208 ON_WM_CHAR()
209 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
210 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
211 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
212 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
213 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
214 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
215 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
216 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
217 ON_WM_TIMER()
218 ON_WM_LBUTTONDBLCLK()
219 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
220 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
221 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
222 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
223 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
224 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
225 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
226 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
227 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
228 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
229 ON_WM_LBUTTONUP()
230 END_MESSAGE_MAP()
233 void CBaseView::DocumentUpdated()
235 ReleaseBitmap();
236 m_nLineHeight = -1;
237 m_nCharWidth = -1;
238 m_nScreenChars = -1;
239 m_nLastScreenChars = -1;
240 m_nMaxLineLength = -1;
241 m_nScreenLines = -1;
242 m_nTopLine = 0;
243 m_bModified = FALSE;
244 m_bOtherDiffChecked = false;
245 m_nDigits = 0;
246 m_nMouseLine = -1;
247 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
248 m_nTabMode = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabMode"), TABMODE_NONE);
249 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
250 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
251 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
252 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
253 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
254 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
255 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
256 m_Eols[EOL_AUTOLINE] = m_Eols[m_lineendings==EOL_AUTOLINE
257 ? EOL_CRLF
258 : m_lineendings];
259 DeleteFonts();
260 ClearCurrentSelection();
261 UpdateStatusBar();
262 Invalidate();
265 void CBaseView::UpdateStatusBar()
267 int nRemovedLines = 0;
268 int nAddedLines = 0;
269 int nConflictedLines = 0;
271 if (m_pViewData)
273 for (int i=0; i<m_pViewData->GetCount(); i++)
275 DiffStates state = m_pViewData->GetState(i);
276 switch (state)
278 case DIFFSTATE_ADDED:
279 case DIFFSTATE_IDENTICALADDED:
280 case DIFFSTATE_THEIRSADDED:
281 case DIFFSTATE_YOURSADDED:
282 case DIFFSTATE_CONFLICTADDED:
283 nAddedLines++;
284 break;
285 case DIFFSTATE_IDENTICALREMOVED:
286 case DIFFSTATE_REMOVED:
287 case DIFFSTATE_THEIRSREMOVED:
288 case DIFFSTATE_YOURSREMOVED:
289 nRemovedLines++;
290 break;
291 case DIFFSTATE_CONFLICTED:
292 case DIFFSTATE_CONFLICTED_IGNORED:
293 nConflictedLines++;
294 break;
299 CString sBarText;
300 CString sTemp;
302 if (m_pwndStatusBar)
304 sBarText += CFileTextLines::GetEncodingName(m_texttype);
305 sBarText += sBarText.IsEmpty() ? L"" : L" ";
306 sBarText += GetEolName(m_lineendings);
307 sBarText += sBarText.IsEmpty() ? L"" : L" ";
309 if (sBarText.IsEmpty())
310 sBarText += _T(" / ");
313 if (nRemovedLines)
315 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
316 if (!sBarText.IsEmpty())
317 sBarText += _T(" / ");
318 sBarText += sTemp;
320 if (nAddedLines)
322 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
323 if (!sBarText.IsEmpty())
324 sBarText += _T(" / ");
325 sBarText += sTemp;
327 if (nConflictedLines)
329 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
330 if (!sBarText.IsEmpty())
331 sBarText += _T(" / ");
332 sBarText += sTemp;
334 if (m_pwndStatusBar || m_pwndRibbonStatusBar)
336 if (m_pwndStatusBar)
338 UINT nID;
339 UINT nStyle;
340 int cxWidth;
341 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
343 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
345 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
347 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
348 sBarText = sTemp+sBarText;
350 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
352 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
353 sBarText = sTemp+sBarText;
355 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
356 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
357 //calculate the width of the text
358 CDC * pDC = m_pwndStatusBar->GetDC();
359 if (pDC)
361 CSize size = pDC->GetTextExtent(sBarText);
362 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
363 ReleaseDC(pDC);
365 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
367 else if (m_pwndRibbonStatusBar)
369 if (!IsViewGood(m_pwndBottom))
370 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
371 if ((m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW) && (IsViewGood(this)))
373 m_pwndRibbonStatusBar->RemoveElement(ID_INDICATOR_BOTTOMVIEW);
374 std::unique_ptr<CMFCRibbonButtonsGroup> apBtnGroupBottom(new CMFCRibbonButtonsGroup);
375 apBtnGroupBottom->SetID(ID_INDICATOR_BOTTOMVIEW);
376 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW)), TRUE));
377 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, L"");
378 m_pMainFrame->FillEncodingButton(pButton, ID_INDICATOR_BOTTOMENCODINGSTART);
379 apBtnGroupBottom->AddButton(pButton);
380 pButton = new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, L"");
381 m_pMainFrame->FillEOLButton(pButton, ID_INDICATOR_BOTTOMEOLSTART);
382 apBtnGroupBottom->AddButton(pButton);
383 apBtnGroupBottom->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW, L"", TRUE));
384 m_pwndRibbonStatusBar->AddExtendedElement(apBtnGroupBottom.release(), L"");
387 CMFCRibbonButtonsGroup * pGroup = DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup, m_pwndRibbonStatusBar->FindByID(m_nStatusBarID));
388 if (pGroup)
390 CMFCRibbonStatusBarPane* pPane = DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane, pGroup->GetButton(3));
391 if (pPane)
393 pPane->SetText(sBarText);
395 CMFCRibbonButton * pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(1));
396 if (pButton)
398 pButton->SetText(CFileTextLines::GetEncodingName(m_texttype));
399 pButton->SetDescription(CFileTextLines::GetEncodingName(m_texttype));
401 pButton = DYNAMIC_DOWNCAST(CMFCRibbonButton, pGroup->GetButton(2));
402 if (pButton)
404 pButton->SetText(GetEolName(m_lineendings));
405 pButton->SetDescription(GetEolName(m_lineendings));
408 m_pwndRibbonStatusBar->RecalcLayout();
409 m_pwndRibbonStatusBar->Invalidate();
414 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
416 if (!CView::PreCreateWindow(cs))
417 return FALSE;
419 cs.dwExStyle |= WS_EX_CLIENTEDGE;
420 cs.style &= ~WS_BORDER;
421 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
422 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
424 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
425 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
427 // View must always create its own scrollbars,
428 // if only it's not used within splitter
429 cs.style |= (WS_HSCROLL | WS_VSCROLL);
431 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
432 return TRUE;
435 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
437 int nIndex = 0;
438 if (bBold)
439 nIndex |= 1;
440 if (bItalic)
441 nIndex |= 2;
442 if (m_apFonts[nIndex] == NULL)
444 m_apFonts[nIndex] = new CFont;
445 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
446 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
447 m_lfBaseFont.lfItalic = (BYTE) bItalic;
448 CDC * pDC = GetDC();
449 if (pDC)
451 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
452 ReleaseDC(pDC);
454 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
455 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
457 delete m_apFonts[nIndex];
458 m_apFonts[nIndex] = NULL;
459 return CView::GetFont();
462 return m_apFonts[nIndex];
465 void CBaseView::CalcLineCharDim()
467 CDC *pDC = GetDC();
468 if (pDC == nullptr)
469 return;
470 CFont *pOldFont = pDC->SelectObject(GetFont());
471 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
472 pDC->SelectObject(pOldFont);
473 ReleaseDC(pDC);
475 m_nLineHeight = szCharExt.cy;
476 if (m_nLineHeight <= 0)
477 m_nLineHeight = -1;
478 m_nCharWidth = szCharExt.cx;
479 if (m_nCharWidth <= 0)
480 m_nCharWidth = -1;
483 int CBaseView::GetScreenChars()
485 if (m_nScreenChars == -1)
487 CRect rect;
488 GetClientRect(&rect);
489 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
490 if (m_nScreenChars < 0)
491 m_nScreenChars = 0;
493 return m_nScreenChars;
496 int CBaseView::GetAllMinScreenChars() const
498 int nChars = INT_MAX;
499 if (IsLeftViewGood())
500 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
501 if (IsRightViewGood())
502 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
503 if (IsBottomViewGood())
504 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
505 return (nChars==INT_MAX) ? 0 : nChars;
508 int CBaseView::GetAllMaxLineLength() const
510 int nLength = 0;
511 if (IsLeftViewGood())
512 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
513 if (IsRightViewGood())
514 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
515 if (IsBottomViewGood())
516 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
517 return nLength;
520 int CBaseView::GetLineHeight()
522 if (m_nLineHeight == -1)
523 CalcLineCharDim();
524 if (m_nLineHeight <= 0)
525 return 1;
526 return m_nLineHeight;
529 int CBaseView::GetCharWidth()
531 if (m_nCharWidth == -1)
532 CalcLineCharDim();
533 if (m_nCharWidth <= 0)
534 return 1;
535 return m_nCharWidth;
538 int CBaseView::GetMaxLineLength()
540 if (m_nMaxLineLength == -1)
542 m_nMaxLineLength = 0;
543 int nLineCount = GetLineCount();
544 for (int i=0; i<nLineCount; i++)
546 int nActualLength = GetLineLength(i);
547 if (m_nMaxLineLength < nActualLength)
548 m_nMaxLineLength = nActualLength;
551 return m_nMaxLineLength;
554 int CBaseView::GetLineLength(int index)
556 if (m_pViewData == NULL)
557 return 0;
558 if (m_pViewData->GetCount() == 0)
559 return 0;
560 if ((int)m_Screen2View.size() <= index)
561 return 0;
562 int viewLine = GetViewLineForScreen(index);
563 if (m_pMainFrame->m_bWrapLines)
565 int nLineLength = GetLineChars(index).GetLength();
566 ASSERT(nLineLength >= 0);
567 return nLineLength;
569 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
570 ASSERT(nLineLength >= 0);
571 return nLineLength;
574 int CBaseView::GetViewLineLength(int nViewLine) const
576 if (m_pViewData == NULL)
577 return 0;
578 if (m_pViewData->GetCount() <= nViewLine)
579 return 0;
580 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
581 ASSERT(nLineLength >= 0);
582 return nLineLength;
585 int CBaseView::GetLineCount() const
587 if (m_pViewData == NULL)
588 return 1;
589 int nLineCount = (int)m_Screen2View.size();
590 ASSERT(nLineCount >= 0);
591 return nLineCount;
594 int CBaseView::GetSubLineOffset(int index)
596 return m_Screen2View.GetSubLineOffset(index);
599 CString CBaseView::GetViewLineChars(int nViewLine) const
601 if (m_pViewData == NULL)
602 return 0;
603 if (m_pViewData->GetCount() <= nViewLine)
604 return 0;
605 return m_pViewData->GetLine(nViewLine);
608 CString CBaseView::GetLineChars(int index)
610 if (m_pViewData == NULL)
611 return 0;
612 if (m_pViewData->GetCount() == 0)
613 return 0;
614 if ((int)m_Screen2View.size() <= index)
615 return 0;
616 int viewLine = GetViewLineForScreen(index);
617 if (m_pMainFrame->m_bWrapLines)
619 int subLine = GetSubLineOffset(index);
620 if (subLine >= 0)
622 if (subLine < CountMultiLines(viewLine))
624 return m_ScreenedViewLine[viewLine].SubLines[subLine];
626 return L"";
629 return m_pViewData->GetLine(viewLine);
632 void CBaseView::CheckOtherView()
634 if (m_bOtherDiffChecked)
635 return;
636 // find out what the 'other' file is
637 m_pOtherViewData = NULL;
638 m_pOtherView = NULL;
639 if (this == m_pwndLeft && IsRightViewGood())
641 m_pOtherViewData = m_pwndRight->m_pViewData;
642 m_pOtherView = m_pwndRight;
645 if (this == m_pwndRight && IsLeftViewGood())
647 m_pOtherViewData = m_pwndLeft->m_pViewData;
648 m_pOtherView = m_pwndLeft;
651 m_bOtherDiffChecked = true;
655 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
657 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
658 ASSERT(viewData);
660 DiffStates origstate = viewData->GetState(nLineIndex);
662 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
663 nStartBlock = nLineIndex;
664 nEndBlock = nLineIndex;
665 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
667 DiffStates state = viewData->GetState(nStartBlock - 1);
668 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
669 origstate = state;
670 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
671 nStartBlock--;
672 else
673 break;
675 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
677 DiffStates state = viewData->GetState(nEndBlock + 1);
678 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
679 origstate = state;
680 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
681 nEndBlock++;
682 else
683 break;
687 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
689 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
691 int len = 0;
692 for (int i = nStartBlock; i <= nEndBlock; ++i)
693 len += viewData->GetLine(i).GetLength()+2;
695 CString block;
696 // do not check for whitespace blocks if the line is too long, because
697 // reserving a lot of memory here takes too much time (performance hog)
698 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
699 return block;
700 block.Preallocate(len+1);
701 for (int i = nStartBlock; i <= nEndBlock; ++i)
703 block += viewData->GetLine(i);
704 block += m_Eols[viewData->GetLineEnding(i)];
706 return block;
709 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical, int& blockstart, int& blockend)
711 if (m_pViewData == NULL)
712 return false;
713 bIdentical = false;
714 CheckOtherView();
715 if (!m_pOtherViewData)
716 return false;
717 int viewLine = GetViewLineForScreen(nLineIndex);
718 if (
719 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
720 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
723 bIdentical = true;
724 return false;
726 // first check whether the line itself only has whitespace changes
727 CString mine = m_pViewData->GetLine(viewLine);
728 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
729 if (mine.IsEmpty() && other.IsEmpty())
731 bIdentical = true;
732 return false;
735 if (mine == other)
737 bIdentical = true;
738 return true;
740 FilterWhitespaces(mine, other);
741 if (mine == other)
742 return true;
744 int nStartBlock2, nEndBlock2;
745 GetWhitespaceBlock(m_pViewData, viewLine, blockstart, blockend);
746 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
747 mine = GetWhitespaceString(m_pViewData, blockstart, blockend);
748 if (mine.IsEmpty())
749 bIdentical = false;
750 else
752 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
753 bIdentical = mine == other;
754 FilterWhitespaces(mine, other);
757 return (!mine.IsEmpty()) && (mine == other);
760 bool CBaseView::IsViewLineHidden(int nViewLine)
762 return IsViewLineHidden(m_pViewData, nViewLine);
765 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
767 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
770 int CBaseView::GetLineNumber(int index) const
772 if (m_pViewData == NULL)
773 return -1;
774 int viewLine = GetViewLineForScreen(index);
775 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
776 return -1;
777 return m_pViewData->GetLineNumber(viewLine);
780 int CBaseView::GetScreenLines()
782 if (m_nScreenLines == -1)
784 SCROLLBARINFO sbi;
785 sbi.cbSize = sizeof(sbi);
786 int scrollBarHeight = 0;
787 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
788 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
789 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
790 scrollBarHeight = 0;
791 CRect rect;
792 GetClientRect(&rect);
793 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
794 if (m_nScreenLines < 0)
795 m_nScreenLines = 0;
797 return m_nScreenLines;
800 int CBaseView::GetAllMinScreenLines() const
802 int nLines = INT_MAX;
803 if (IsLeftViewGood())
804 nLines = m_pwndLeft->GetScreenLines();
805 if (IsRightViewGood())
806 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
807 if (IsBottomViewGood())
808 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
809 return (nLines==INT_MAX) ? 0 : nLines;
812 int CBaseView::GetAllLineCount() const
814 int nLines = 0;
815 if (IsLeftViewGood())
816 nLines = m_pwndLeft->GetLineCount();
817 if (IsRightViewGood())
818 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
819 if (IsBottomViewGood())
820 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
821 return nLines;
824 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
826 if (IsLeftViewGood())
827 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
828 if (IsRightViewGood())
829 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
830 if (IsBottomViewGood())
831 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
834 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
836 SCROLLINFO si;
837 si.cbSize = sizeof(si);
838 if (bPositionOnly)
840 si.fMask = SIF_POS;
841 si.nPos = m_nTopLine;
843 else
845 EnableScrollBarCtrl(SB_VERT, TRUE);
846 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
848 m_nTopLine = 0;
849 Invalidate();
851 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
852 si.nMin = 0;
853 si.nMax = GetAllLineCount();
854 si.nPage = GetAllMinScreenLines();
855 si.nPos = m_nTopLine;
857 VERIFY(SetScrollInfo(SB_VERT, &si));
860 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
862 CView::OnVScroll(nSBCode, nPos, pScrollBar);
863 if (m_pwndLeft)
864 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
865 if (m_pwndRight)
866 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
867 if (m_pwndBottom)
868 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
869 if (m_pwndLocator)
870 m_pwndLocator->Invalidate();
873 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
875 // Note we cannot use nPos because of its 16-bit nature
876 SCROLLINFO si;
877 si.cbSize = sizeof(si);
878 si.fMask = SIF_ALL;
879 VERIFY(master->GetScrollInfo(SB_VERT, &si));
881 int nPageLines = GetScreenLines();
882 int nLineCount = GetLineCount();
884 int nNewTopLine;
886 static LONG textwidth = 0;
887 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
888 switch (nSBCode)
890 case SB_TOP:
891 nNewTopLine = 0;
892 break;
893 case SB_BOTTOM:
894 nNewTopLine = nLineCount - nPageLines + 1;
895 break;
896 case SB_LINEUP:
897 nNewTopLine = m_nTopLine - 1;
898 break;
899 case SB_LINEDOWN:
900 nNewTopLine = m_nTopLine + 1;
901 break;
902 case SB_PAGEUP:
903 nNewTopLine = m_nTopLine - si.nPage + 1;
904 break;
905 case SB_PAGEDOWN:
906 nNewTopLine = m_nTopLine + si.nPage - 1;
907 break;
908 case SB_THUMBPOSITION:
909 m_ScrollTool.Clear();
910 nNewTopLine = si.nTrackPos;
911 textwidth = 0;
912 break;
913 case SB_THUMBTRACK:
914 nNewTopLine = si.nTrackPos;
915 if (GetFocus() == this)
917 RECT thumbrect;
918 GetClientRect(&thumbrect);
919 ClientToScreen(&thumbrect);
921 POINT thumbpoint;
922 thumbpoint.x = thumbrect.right;
923 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
924 m_ScrollTool.Init(&thumbpoint);
925 if (textwidth == 0)
927 CString sTemp = sFormat;
928 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
929 textwidth = m_ScrollTool.GetTextWidth(sTemp);
931 thumbpoint.x -= textwidth;
932 int line = GetLineNumber(nNewTopLine);
933 if (line >= 0)
934 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
935 else
936 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
938 break;
939 default:
940 return;
943 if (nNewTopLine < 0)
944 nNewTopLine = 0;
945 if (nNewTopLine >= nLineCount)
946 nNewTopLine = nLineCount - 1;
947 ScrollToLine(nNewTopLine);
950 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
952 if (IsLeftViewGood())
953 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
954 if (IsRightViewGood())
955 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
956 if (IsBottomViewGood())
957 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
960 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
962 SCROLLINFO si;
963 si.cbSize = sizeof(si);
964 if (bPositionOnly)
966 si.fMask = SIF_POS;
967 si.nPos = m_nOffsetChar;
969 else
971 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
972 if (!m_pMainFrame->m_bWrapLines)
974 int minScreenChars = GetAllMinScreenChars();
975 int maxLineLength = GetAllMaxLineLength();
976 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
978 m_nOffsetChar = 0;
979 Invalidate();
981 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
982 si.nMin = 0;
983 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
984 si.nMax += GetMarginWidth()/GetCharWidth();
985 si.nPage = minScreenChars;
986 si.nPos = m_nOffsetChar;
989 VERIFY(SetScrollInfo(SB_HORZ, &si));
992 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
994 CView::OnHScroll(nSBCode, nPos, pScrollBar);
995 if (m_pwndLeft)
996 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
997 if (m_pwndRight)
998 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
999 if (m_pwndBottom)
1000 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
1001 if (m_pwndLocator)
1002 m_pwndLocator->Invalidate();
1005 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
1007 SCROLLINFO si;
1008 si.cbSize = sizeof(si);
1009 si.fMask = SIF_ALL;
1010 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
1012 int nPageChars = GetScreenChars();
1013 int nMaxLineLength = GetMaxLineLength();
1015 int nNewOffset;
1016 switch (nSBCode)
1018 case SB_LEFT:
1019 nNewOffset = 0;
1020 break;
1021 case SB_BOTTOM:
1022 nNewOffset = nMaxLineLength - nPageChars + 1;
1023 break;
1024 case SB_LINEUP:
1025 nNewOffset = m_nOffsetChar - 1;
1026 break;
1027 case SB_LINEDOWN:
1028 nNewOffset = m_nOffsetChar + 1;
1029 break;
1030 case SB_PAGEUP:
1031 nNewOffset = m_nOffsetChar - si.nPage + 1;
1032 break;
1033 case SB_PAGEDOWN:
1034 nNewOffset = m_nOffsetChar + si.nPage - 1;
1035 break;
1036 case SB_THUMBPOSITION:
1037 case SB_THUMBTRACK:
1038 nNewOffset = si.nTrackPos;
1039 break;
1040 default:
1041 return;
1044 if (nNewOffset >= nMaxLineLength)
1045 nNewOffset = nMaxLineLength - 1;
1046 if (nNewOffset < 0)
1047 nNewOffset = 0;
1048 ScrollToChar(nNewOffset, TRUE);
1051 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1053 if (m_nOffsetChar != nNewOffsetChar)
1055 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1056 m_nOffsetChar = nNewOffsetChar;
1057 CRect rcScroll;
1058 GetClientRect(&rcScroll);
1059 rcScroll.left += GetMarginWidth();
1060 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1061 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1062 // update the view header
1063 rcScroll.left = 0;
1064 rcScroll.top = 0;
1065 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1066 InvalidateRect(&rcScroll, FALSE);
1067 UpdateWindow();
1068 if (bTrackScrollBar)
1069 RecalcHorzScrollBar(TRUE);
1070 UpdateCaret();
1071 if (m_pwndLineDiffBar)
1072 m_pwndLineDiffBar->Invalidate();
1076 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1078 if (m_pwndLeft)
1079 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1080 if (m_pwndRight)
1081 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1082 if (m_pwndBottom)
1083 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1086 void CBaseView::ScrollAllSide(int delta)
1088 int nNewOffset = m_nOffsetChar;
1089 nNewOffset += delta;
1090 int nMaxLineLength = GetMaxLineLength();
1091 if (nNewOffset >= nMaxLineLength)
1092 nNewOffset = nMaxLineLength - 1;
1093 if (nNewOffset < 0)
1094 nNewOffset = 0;
1095 ScrollAllToChar(nNewOffset, TRUE);
1096 if (m_pwndLineDiffBar)
1097 m_pwndLineDiffBar->Invalidate();
1098 UpdateCaret();
1101 void CBaseView::ScrollSide(int delta)
1103 int nNewOffset = m_nOffsetChar;
1104 nNewOffset += delta;
1105 int nMaxLineLength = GetMaxLineLength();
1106 if (nNewOffset >= nMaxLineLength)
1107 nNewOffset = nMaxLineLength - 1;
1108 if (nNewOffset < 0)
1109 nNewOffset = 0;
1110 ScrollToChar(nNewOffset, TRUE);
1111 if (m_pwndLineDiffBar)
1112 m_pwndLineDiffBar->Invalidate();
1113 UpdateCaret();
1116 void CBaseView::ScrollVertical(short zDelta)
1118 const int nLineCount = GetLineCount();
1119 int nTopLine = m_nTopLine;
1120 nTopLine -= (zDelta/30);
1121 if (nTopLine < 0)
1122 nTopLine = 0;
1123 if (nTopLine >= nLineCount)
1124 nTopLine = nLineCount - 1;
1125 ScrollToLine(nTopLine, TRUE);
1128 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1130 if (m_nTopLine != nNewTopLine)
1132 if (nNewTopLine < 0)
1133 nNewTopLine = 0;
1135 int nScrollLines = m_nTopLine - nNewTopLine;
1137 m_nTopLine = nNewTopLine;
1138 CRect rcScroll;
1139 GetClientRect(&rcScroll);
1140 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1141 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1142 UpdateWindow();
1143 if (bTrackScrollBar)
1144 RecalcVertScrollBar(TRUE);
1145 UpdateCaret();
1150 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1152 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1154 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1156 int nViewLine = GetViewLineForScreen(nLineIndex);
1157 HICON icon = NULL;
1158 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1159 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1160 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1162 DiffStates state = m_pViewData->GetState(nViewLine);
1163 switch (state)
1165 case DIFFSTATE_ADDED:
1166 case DIFFSTATE_THEIRSADDED:
1167 case DIFFSTATE_YOURSADDED:
1168 case DIFFSTATE_IDENTICALADDED:
1169 case DIFFSTATE_CONFLICTADDED:
1170 eIcon = TScreenedViewLine::ICN_ADD;
1171 break;
1172 case DIFFSTATE_REMOVED:
1173 case DIFFSTATE_THEIRSREMOVED:
1174 case DIFFSTATE_YOURSREMOVED:
1175 case DIFFSTATE_IDENTICALREMOVED:
1176 eIcon = TScreenedViewLine::ICN_REMOVED;
1177 break;
1178 case DIFFSTATE_CONFLICTED:
1179 eIcon = TScreenedViewLine::ICN_CONFLICT;
1180 break;
1181 case DIFFSTATE_CONFLICTED_IGNORED:
1182 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1183 break;
1184 case DIFFSTATE_EDITED:
1185 eIcon = TScreenedViewLine::ICN_EDIT;
1186 break;
1187 default:
1188 break;
1190 bool bIdentical = false;
1191 int blockstart = -1;
1192 int blockend = -1;
1193 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical, blockstart, blockend)))
1195 if (bIdentical)
1196 eIcon = TScreenedViewLine::ICN_SAME;
1197 else
1198 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1199 if (((blockstart >= 0) && (blockend >= 0)) && (blockstart < blockend))
1201 if (nViewLine > blockstart)
1202 Invalidate(); // redraw the upper icons since they're now changing
1203 while (blockstart <= blockend)
1204 m_ScreenedViewLine[blockstart++].eIcon = eIcon;
1207 if (m_pViewData->GetMovedIndex(nViewLine) >= 0)
1208 eIcon = TScreenedViewLine::ICN_MOVED;
1209 if (m_pViewData->GetMarked(nViewLine))
1210 eIcon = TScreenedViewLine::ICN_MARKED;
1211 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1213 switch (eIcon)
1215 case TScreenedViewLine::ICN_UNKNOWN:
1216 case TScreenedViewLine::ICN_NONE:
1217 break;
1218 case TScreenedViewLine::ICN_SAME:
1219 icon = m_hEqualIcon;
1220 break;
1221 case TScreenedViewLine::ICN_EDIT:
1222 icon = m_hEditedIcon;
1223 break;
1224 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1225 icon = m_hWhitespaceBlockIcon;
1226 break;
1227 case TScreenedViewLine::ICN_ADD:
1228 icon = m_hAddedIcon;
1229 break;
1230 case TScreenedViewLine::ICN_CONFLICT:
1231 icon = m_hConflictedIcon;
1232 break;
1233 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1234 icon = m_hConflictedIgnoredIcon;
1235 break;
1236 case TScreenedViewLine::ICN_REMOVED:
1237 icon = m_hRemovedIcon;
1238 break;
1239 case TScreenedViewLine::ICN_MOVED:
1240 icon = m_hMovedIcon;
1241 break;
1242 case TScreenedViewLine::ICN_MARKED:
1243 icon = m_hMarkedIcon;
1244 break;
1248 if (icon)
1250 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1252 if ((m_bViewLinenumbers)&&(m_nDigits))
1254 int nSubLine = GetSubLineOffset(nLineIndex);
1255 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1256 CString sLinenumber;
1257 if (bIsFirstSubline)
1259 CString sLinenumberFormat;
1260 int nLineNumber = GetLineNumber(nLineIndex);
1261 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1263 // TODO: do not show if there is no number hidden
1264 // TODO: show number if there is only one
1265 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1266 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1268 else if (nLineNumber >= 0)
1270 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1271 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1273 else if (m_pMainFrame->m_bWrapLines)
1275 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1276 sLinenumber.Format(sLinenumberFormat, _T("·"));
1278 if (!sLinenumber.IsEmpty())
1280 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1281 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1283 pdc->SelectObject(GetFont());
1284 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1291 int CBaseView::GetMarginWidth()
1293 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1295 if (m_nDigits <= 0)
1297 int nLength = (int)m_pViewData->GetCount();
1298 // find out how many digits are needed to show the highest line number
1299 CString sMax;
1300 sMax.Format(_T("%d"), nLength);
1301 m_nDigits = sMax.GetLength();
1303 int nWidth = GetCharWidth();
1304 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1306 return MARGINWIDTH;
1309 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1311 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1312 COLORREF crBk, crFg;
1313 if (IsBottomViewGood())
1315 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1316 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1318 else
1320 DiffStates state = DIFFSTATE_REMOVED;
1321 if (this == m_pwndRight)
1323 state = DIFFSTATE_ADDED;
1325 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1327 pdc->SetBkColor(crBk);
1328 pdc->FillSolidRect(textrect, crBk);
1330 pdc->SetTextColor(crFg);
1332 pdc->SelectObject(GetFont(FALSE, TRUE));
1334 CString sViewTitle;
1335 if (IsModified())
1337 sViewTitle = _T("* ") + m_sWindowName;
1339 else
1341 sViewTitle = m_sWindowName;
1343 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1344 if (nStringLength > rect.Width())
1346 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1347 sViewTitle = m_sWindowName.Mid(offset);
1349 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1350 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1351 if (this->GetFocus() == this)
1352 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1353 else
1354 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1357 void CBaseView::OnDraw(CDC * pDC)
1359 CRect rcClient;
1360 GetClientRect(rcClient);
1362 int nLineCount = GetLineCount();
1363 int nLineHeight = GetLineHeight();
1365 CDC cacheDC;
1366 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1367 if (m_pCacheBitmap == NULL)
1369 m_pCacheBitmap = new CBitmap;
1370 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1372 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1374 DrawHeader(pDC, rcClient);
1376 CRect rcLine;
1377 rcLine = rcClient;
1378 rcLine.top += nLineHeight+HEADERHEIGHT;
1379 rcLine.bottom = rcLine.top + nLineHeight;
1380 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1381 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1383 int nCurrentLine = m_nTopLine;
1384 bool bBeyondFileLineCached = false;
1385 while (rcLine.top < rcClient.bottom)
1387 if (nCurrentLine < nLineCount)
1389 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1390 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1391 bBeyondFileLineCached = false;
1393 else if (!bBeyondFileLineCached)
1395 DrawMargin(&cacheDC, rcCacheMargin, -1);
1396 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1397 bBeyondFileLineCached = true;
1400 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1402 nCurrentLine ++;
1403 rcLine.OffsetRect(0, nLineHeight);
1406 cacheDC.SelectObject(pOldBitmap);
1407 cacheDC.DeleteDC();
1410 bool CBaseView::IsStateConflicted(DiffStates state)
1412 switch (state)
1414 case DIFFSTATE_CONFLICTED:
1415 case DIFFSTATE_CONFLICTED_IGNORED:
1416 case DIFFSTATE_CONFLICTEMPTY:
1417 case DIFFSTATE_CONFLICTADDED:
1418 return true;
1420 return false;
1423 bool CBaseView::IsStateEmpty(DiffStates state)
1425 switch (state)
1427 case DIFFSTATE_CONFLICTEMPTY:
1428 case DIFFSTATE_UNKNOWN:
1429 case DIFFSTATE_EMPTY:
1430 return true;
1432 return false;
1435 bool CBaseView::IsStateRemoved(DiffStates state)
1437 switch (state)
1439 case DIFFSTATE_REMOVED:
1440 case DIFFSTATE_THEIRSREMOVED:
1441 case DIFFSTATE_YOURSREMOVED:
1442 case DIFFSTATE_IDENTICALREMOVED:
1443 return true;
1445 return false;
1448 DiffStates CBaseView::ResolveState(DiffStates state)
1450 if (IsStateConflicted(state))
1452 if (state == DIFFSTATE_CONFLICTEMPTY)
1453 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1454 else
1455 return DIFFSTATE_CONFLICTRESOLVED;
1457 return state;
1461 bool CBaseView::IsLineEmpty(int nLineIndex)
1463 if (m_pViewData == 0)
1464 return FALSE;
1465 int nViewLine = GetViewLineForScreen(nLineIndex);
1466 return IsViewLineEmpty(nViewLine);
1469 bool CBaseView::IsViewLineEmpty(int nViewLine)
1471 if (m_pViewData == 0)
1472 return FALSE;
1473 const DiffStates state = m_pViewData->GetState(nViewLine);
1474 return IsStateEmpty(state);
1477 bool CBaseView::IsLineRemoved(int nLineIndex)
1479 if (m_pViewData == 0)
1480 return FALSE;
1481 int nViewLine = GetViewLineForScreen(nLineIndex);
1482 return IsViewLineRemoved(nViewLine);
1485 bool CBaseView::IsViewLineRemoved(int nViewLine)
1487 if (m_pViewData == 0)
1488 return FALSE;
1489 const DiffStates state = m_pViewData->GetState(nViewLine);
1490 return IsStateRemoved(state);
1493 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1495 if (m_pViewData == 0)
1496 return false;
1497 const DiffStates state = m_pViewData->GetState(nLineIndex);
1498 return IsStateConflicted(state);
1501 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1503 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1506 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1508 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1511 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1513 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1514 return;
1515 int viewLine = GetViewLineForScreen(nLineIndex);
1516 EOL ending = m_pViewData->GetLineEnding(viewLine);
1517 if (m_bIconLFs)
1519 HICON hEndingIcon = NULL;
1520 switch (ending)
1522 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1523 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1524 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1525 default: return;
1527 if (origin.x < (rc.left-GetCharWidth()))
1528 return;
1529 // If EOL style has changed, color end-of-line markers as inline differences.
1531 m_bShowInlineDiff && m_pOtherViewData &&
1532 (viewLine < m_pOtherViewData->GetCount()) &&
1533 (ending != EOL_NOENDING) &&
1534 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1535 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1538 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1541 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1543 else
1545 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1546 CPen * oldpen = pDC->SelectObject(&pen);
1547 int yMiddle = origin.y + rc.Height()/2;
1548 int xMiddle = origin.x+GetCharWidth()/2;
1549 bool bMultiline = false;
1550 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1552 if (GetLineLength(nLineIndex+1))
1554 // multiline
1555 bMultiline = true;
1556 pDC->MoveTo(origin.x, yMiddle-2);
1557 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1558 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1559 pDC->LineTo(origin.x, yMiddle+2);
1561 else if (GetLineLength(nLineIndex) == 0)
1562 bMultiline = true;
1564 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1565 bMultiline = true;
1567 if (!bMultiline)
1569 switch (ending)
1571 case EOL_AUTOLINE:
1572 case EOL_CRLF:
1573 // arrow from top to middle+2, then left
1574 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1575 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1576 case EOL_CR:
1577 // arrow from right to left
1578 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1579 pDC->LineTo(origin.x, yMiddle);
1580 pDC->LineTo(origin.x+4, yMiddle+4);
1581 pDC->MoveTo(origin.x, yMiddle);
1582 pDC->LineTo(origin.x+4, yMiddle-4);
1583 break;
1584 case EOL_LFCR:
1585 // from right-upper to left then down
1586 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1587 pDC->LineTo(xMiddle, yMiddle-2);
1588 pDC->LineTo(xMiddle, rc.bottom-1);
1589 pDC->LineTo(xMiddle+4, rc.bottom-5);
1590 pDC->MoveTo(xMiddle, rc.bottom-1);
1591 pDC->LineTo(xMiddle-4, rc.bottom-5);
1592 break;
1593 case EOL_LF:
1594 // arrow from top to bottom
1595 pDC->MoveTo(xMiddle, rc.top);
1596 pDC->LineTo(xMiddle, rc.bottom-1);
1597 pDC->LineTo(xMiddle+4, rc.bottom-5);
1598 pDC->MoveTo(xMiddle, rc.bottom-1);
1599 pDC->LineTo(xMiddle-4, rc.bottom-5);
1600 break;
1601 case EOL_FF: // Form Feed, U+000C
1602 case EOL_NEL: // Next Line, U+0085
1603 case EOL_LS: // Line Separator, U+2028
1604 case EOL_PS: // Paragraph Separator, U+2029
1605 // draw a horizontal line at the bottom of this line
1606 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1607 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1608 pDC->LineTo(origin.x, rc.bottom-2);
1609 pDC->LineTo(origin.x+5, rc.bottom-2);
1610 pDC->MoveTo(origin.x, rc.bottom-2);
1611 pDC->LineTo(origin.x+1, rc.bottom-6);
1612 break;
1613 default: // other EOLs
1614 // arrow from top right to bottom left
1615 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1616 pDC->LineTo(origin.x, rc.bottom-1);
1617 pDC->LineTo(origin.x+5, rc.bottom-2);
1618 pDC->MoveTo(origin.x, rc.bottom-1);
1619 pDC->LineTo(origin.x+1, rc.bottom-6);
1620 break;
1621 case EOL_NOENDING:
1622 break;
1625 pDC->SelectObject(oldpen);
1629 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1631 if (!m_bShowSelection)
1632 return;
1634 int nSelBlockStart;
1635 int nSelBlockEnd;
1636 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1637 return;
1639 const int THICKNESS = 2;
1640 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1642 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1643 int nSubLine = GetSubLineOffset(nLineIndex);
1644 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1645 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1647 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1650 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1651 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1653 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1657 void CBaseView::DrawTextLine(
1658 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1660 ASSERT(nLineIndex < GetLineCount());
1661 int nViewLine = GetViewLineForScreen(nLineIndex);
1662 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1664 LineColors lineCols = GetLineColors(nViewLine);
1666 CString sViewLine = GetViewLineChars(nViewLine);
1667 // mark selection
1668 if (m_bShowSelection && HasTextSelection())
1670 // has this line selection ?
1671 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1673 int nViewLineLength = sViewLine.GetLength();
1675 // first suppose the whole line is selected
1676 int selectedStart = 0;
1677 int selectedEnd = nViewLineLength;
1679 // the view line is partially selected
1680 if (m_ptSelectionViewPosStart.y == nViewLine)
1682 selectedStart = m_ptSelectionViewPosStart.x;
1685 if (m_ptSelectionViewPosEnd.y == nViewLine)
1687 selectedEnd = m_ptSelectionViewPosEnd.x;
1689 // apply selection coloring
1690 // First enforce start and end point
1691 lineCols.SplitBlock(selectedStart);
1692 lineCols.SplitBlock(selectedEnd);
1693 // change color of affected parts
1694 long intenseColorScale = m_bFocused ? 70 : 30;
1695 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1696 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1698 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1699 if (it->second.shot == it->second.background)
1701 it->second.shot = crBk;
1703 it->second.background = crBk;
1704 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1709 // TODO: remove duplicate from selection and mark
1710 if (!m_sMarkedWord.IsEmpty())
1712 int nMarkLength = m_sMarkedWord.GetLength();
1713 //int nViewLineLength = sViewLine.GetLength();
1714 const TCHAR * text = sViewLine;
1715 const TCHAR * findText = text;
1716 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1718 int nMarkStart = static_cast<int>(findText - text);
1719 int nMarkEnd = nMarkStart + nMarkLength;
1720 findText += nMarkLength;
1721 ECharGroup eLeft = GetCharGroup(sViewLine, nMarkStart - 1);
1722 ECharGroup eStart = GetCharGroup(sViewLine, nMarkStart);
1723 if (eLeft == eStart)
1724 continue;
1725 ECharGroup eRight = GetCharGroup(sViewLine, nMarkEnd);
1726 ECharGroup eEnd = GetCharGroup(sViewLine, nMarkEnd - 1);
1727 if (eRight == eEnd)
1728 continue;
1730 // First enforce start and end point
1731 lineCols.SplitBlock(nMarkStart);
1732 lineCols.SplitBlock(nMarkEnd);
1733 // change color of affected parts
1734 const long int nIntenseColorScale = 200;
1735 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1736 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1738 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1739 if (it->second.shot == it->second.background)
1741 it->second.shot = crBk;
1743 it->second.background = crBk;
1744 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1748 if (!m_sFindText.IsEmpty())
1750 int nMarkStart = 0;
1751 int nMarkEnd = 0;
1752 int nStringPos = nMarkStart;
1753 CString searchLine = sViewLine;
1754 if (!m_bMatchCase)
1755 searchLine.MakeLower();
1756 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1758 // First enforce start and end point
1759 lineCols.SplitBlock(nMarkStart+nStringPos);
1760 lineCols.SplitBlock(nMarkEnd+nStringPos);
1761 // change color of affected parts
1762 const long int nIntenseColorScale = 30;
1763 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1764 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1766 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1767 if (it->second.shot == it->second.background)
1769 it->second.shot = crBk;
1771 it->second.background = crBk;
1772 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1774 searchLine = searchLine.Mid(nMarkEnd);
1775 nStringPos = nMarkEnd;
1776 nMarkStart = 0;
1777 nMarkEnd = 0;
1781 // @ this point we may cache data for next line which may be same in wrapped mode
1783 int nTextOffset = 0;
1784 int nSubline = GetSubLineOffset(nLineIndex);
1785 for (int n=0; n<nSubline; n++)
1787 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1788 nTextOffset += sLine.GetLength();
1791 CString sLine = GetLineChars(nLineIndex);
1792 int nLineLength = sLine.GetLength();
1793 CString sLineExp = ExpandChars(sLine);
1794 LPCTSTR textExp = sLineExp;
1795 //int nLineLengthExp = sLineExp.GetLength();
1796 int nStartExp = 0;
1797 int nLeft = coords.x;
1798 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1800 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1801 ++itEnd;
1802 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1803 int nEnd = nLineLength;
1804 if (itEnd != lineCols.end())
1806 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1808 int nBlockLength = nEnd - nStart;
1809 if (nBlockLength > 0 && nEnd>=0)
1811 pDC->SetBkColor(itStart->second.background);
1812 pDC->SetTextColor(itStart->second.text);
1813 int nEndExp = CountExpandedChars(sLine, nEnd);
1814 int nTextLength = nEndExp - nStartExp;
1815 LPCTSTR p_zBlockText = textExp + nStartExp;
1816 SIZE Size;
1817 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1818 int nRight = nLeft + Size.cx;
1819 if ((nRight > rc.left) && (nLeft < rc.right))
1821 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1822 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1823 // is 4094 (4095 doesn't work anymore).
1824 // So we limit the length here to that 4094 chars.
1825 // In case we're scrolled to the right, there's no need to draw the string
1826 // from way outside our window, so we also offset the drawing to the start of the window.
1827 // This reduces the string length as well.
1828 int offset = 0;
1829 int leftcoord = nLeft;
1830 if (nLeft < 0)
1832 offset = (-nLeft/GetCharWidth());
1833 nTextLength -= offset;
1834 leftcoord = nLeft % GetCharWidth();
1837 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1838 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1840 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1843 nLeft = nRight;
1844 coords.x = nRight;
1845 nStartExp = nEndExp;
1850 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1852 if (nLineIndex >= GetLineCount())
1853 nLineIndex = -1;
1854 ASSERT(nLineIndex >= -1);
1856 if ((nLineIndex == -1) || !m_pViewData)
1858 // Draw line beyond the text
1859 COLORREF crBkgnd, crText;
1860 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1861 pDC->FillSolidRect(rc, crBkgnd);
1862 return;
1865 int viewLine = GetViewLineForScreen(nLineIndex);
1866 if (m_pMainFrame->m_bCollapsed)
1868 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1870 COLORREF crBkgnd, crText;
1871 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1872 pDC->FillSolidRect(rc, crBkgnd);
1874 const int THICKNESS = 2;
1875 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1876 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1877 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1878 pDC->SetBkColor(crBkgnd);
1879 CRect rect = rc;
1880 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1881 return;
1885 DiffStates diffState = m_pViewData->GetState(viewLine);
1886 COLORREF crBkgnd, crText;
1887 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1889 if (diffState == DIFFSTATE_CONFLICTED)
1891 // conflicted lines are shown without 'text' on them
1892 CRect rect = rc;
1893 pDC->FillSolidRect(rc, crBkgnd);
1894 // now draw some faint text patterns
1895 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1896 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1897 DrawBlockLine(pDC, rc, nLineIndex);
1898 return;
1901 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1902 CString sLine = GetLineChars(nLineIndex);
1903 if (sLine.IsEmpty())
1905 pDC->FillSolidRect(rc, crBkgnd);
1906 DrawBlockLine(pDC, rc, nLineIndex);
1907 DrawLineEnding(pDC, rc, nLineIndex, origin);
1908 return;
1911 CheckOtherView();
1913 // Draw the line
1915 pDC->SelectObject(GetFont(FALSE, FALSE));
1917 DrawTextLine(pDC, rc, nLineIndex, origin);
1919 // draw white space after the end of line
1920 CRect frect = rc;
1921 if (origin.x > frect.left)
1922 frect.left = origin.x;
1923 if (frect.right > frect.left)
1924 pDC->FillSolidRect(frect, crBkgnd);
1926 // draw the whitespace chars
1927 LPCTSTR pszChars = (LPCWSTR)sLine;
1928 if (m_bViewWhitespace)
1930 int xpos = 0;
1931 int y = rc.top + (rc.bottom-rc.top)/2;
1933 int nActualOffset = 0;
1934 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1936 if (*pszChars == _T('\t'))
1937 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1938 else
1939 nActualOffset++;
1940 pszChars++;
1942 if (nActualOffset > m_nOffsetChar)
1943 pszChars--;
1945 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1946 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1947 while (*pszChars)
1949 switch (*pszChars)
1951 case _T('\t'):
1953 // draw an arrow
1954 CPen * oldPen = pDC->SelectObject(&pen);
1955 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1956 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1957 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1958 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1959 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1960 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1961 xpos += nSpaces;
1962 pDC->SelectObject(oldPen);
1964 break;
1965 case _T(' '):
1967 // draw a small dot
1968 CPen * oldPen = pDC->SelectObject(&pen2);
1969 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1970 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1971 xpos++;
1972 pDC->SelectObject(oldPen);
1974 break;
1975 default:
1976 xpos++;
1977 break;
1979 pszChars++;
1982 DrawBlockLine(pDC, rc, nLineIndex);
1983 if (origin.x >= rc.left)
1984 DrawLineEnding(pDC, rc, nLineIndex, origin);
1987 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1989 if (nCount <= 0)
1991 line = _T("");
1992 return;
1995 int nTabSize = GetTabSize();
1997 int nActualOffset = CountExpandedChars(sLine, nOffset);
1999 LPCTSTR pszChars = (LPCWSTR)sLine;
2000 pszChars += nOffset;
2001 int nLength = nCount;
2003 int nTabCount = 0;
2004 for (int i=0; i<nLength; i++)
2006 if (pszChars[i] == _T('\t'))
2007 nTabCount ++;
2010 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
2011 int nCurPos = 0;
2012 if (nTabCount > 0 || m_bViewWhitespace)
2014 for (int i=0; i<nLength; i++)
2016 if (pszChars[i] == _T('\t'))
2018 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
2019 while (nSpaces > 0)
2021 pszBuf[nCurPos ++] = _T(' ');
2022 nSpaces --;
2025 else
2027 pszBuf[nCurPos] = pszChars[i];
2028 nCurPos ++;
2032 else
2034 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
2035 nCurPos = nLength;
2037 pszBuf[nCurPos] = 0;
2038 line.ReleaseBuffer();
2041 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2043 CString sRet;
2044 int nLength = sLine.GetLength();
2045 ExpandChars(sLine, nOffset, nLength, sRet);
2046 return sRet;
2049 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2051 int nTabSize = GetTabSize();
2053 int nActualOffset = 0;
2054 for (int i=0; i<nLength; i++)
2056 if (sLine[i] == _T('\t'))
2057 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2058 else
2059 nActualOffset ++;
2061 return nActualOffset;
2064 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2066 if (m_pwndLeft)
2067 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2068 if (m_pwndRight)
2069 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2070 if (m_pwndBottom)
2071 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2072 if (m_pwndLocator)
2073 m_pwndLocator->Invalidate();
2076 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2078 //almost the same as ScrollAllToLine, but try to put the line in the
2079 //middle of the view, not on top
2080 int nNewTopLine = nNewLine - GetScreenLines()/2;
2081 if (nNewTopLine < 0)
2082 nNewTopLine = 0;
2083 if (nNewTopLine >= (int)m_Screen2View.size())
2084 nNewTopLine = (int)m_Screen2View.size()-1;
2085 if (bAll)
2086 ScrollAllToLine(nNewTopLine);
2087 else
2088 ScrollToLine(nNewTopLine);
2091 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2093 return TRUE;
2096 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2098 if (CView::OnCreate(lpCreateStruct) == -1)
2099 return -1;
2101 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2102 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2103 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2104 m_lfBaseFont.lfHeight = 0;
2105 m_lfBaseFont.lfWeight = FW_NORMAL;
2106 m_lfBaseFont.lfItalic = FALSE;
2107 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2108 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2109 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2110 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2111 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2113 return 0;
2116 void CBaseView::OnDestroy()
2118 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2120 m_pFindDialog->SendMessage(WM_CLOSE);
2121 return;
2123 CView::OnDestroy();
2124 DeleteFonts();
2125 ReleaseBitmap();
2128 void CBaseView::OnSize(UINT nType, int cx, int cy)
2130 CView::OnSize(nType, cx, cy);
2131 ReleaseBitmap();
2133 m_nScreenLines = -1;
2134 m_nScreenChars = -1;
2135 if (m_nLastScreenChars != GetScreenChars())
2137 BuildAllScreen2ViewVector();
2138 m_nLastScreenChars = m_nScreenChars;
2139 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2141 // if we're in wrap mode, the line wrapping most likely changed
2142 // and that means we have to redraw the whole window, not just the
2143 // scrolled part.
2144 Invalidate(FALSE);
2146 else
2148 // make sure the view header is redrawn
2149 CRect rcScroll;
2150 GetClientRect(&rcScroll);
2151 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2152 InvalidateRect(&rcScroll, FALSE);
2155 else
2157 // make sure the view header is redrawn
2158 CRect rcScroll;
2159 GetClientRect(&rcScroll);
2160 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2161 InvalidateRect(&rcScroll, FALSE);
2163 UpdateLocator();
2164 RecalcVertScrollBar();
2165 RecalcHorzScrollBar();
2167 UpdateCaret();
2170 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2172 if (m_pwndLeft)
2173 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2174 if (m_pwndRight)
2175 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2176 if (m_pwndBottom)
2177 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2178 if (m_pwndLocator)
2179 m_pwndLocator->Invalidate();
2180 return CView::OnMouseWheel(nFlags, zDelta, pt);
2183 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2185 if (m_pwndLeft)
2186 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2187 if (m_pwndRight)
2188 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2189 if (m_pwndBottom)
2190 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2191 if (m_pwndLocator)
2192 m_pwndLocator->Invalidate();
2195 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2197 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2198 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2200 if (bControl || bShift)
2202 if (m_pMainFrame->m_bWrapLines)
2203 return;
2204 // Ctrl-Wheel scrolls sideways
2205 ScrollSide(-zDelta/30);
2207 else
2209 ScrollVertical(zDelta);
2213 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2215 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2216 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2218 if (bControl || bShift)
2220 ScrollVertical(zDelta);
2222 else
2224 if (m_pMainFrame->m_bWrapLines)
2225 return;
2226 // Ctrl-Wheel scrolls sideways
2227 ScrollSide(-zDelta/30);
2231 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2233 if (nHitTest == HTCLIENT)
2235 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2237 if (m_nMouseLine < (int)m_Screen2View.size())
2239 if (m_nMouseLine >= 0)
2241 int viewLine = GetViewLineForScreen(m_nMouseLine);
2242 if (viewLine < m_pViewData->GetCount())
2244 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2246 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2247 return TRUE;
2253 if (m_mouseInMargin)
2255 ::SetCursor(m_margincursor);
2256 return TRUE;
2258 if (m_nMouseLine >= 0)
2260 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2261 return TRUE;
2264 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2265 return TRUE;
2267 return CView::OnSetCursor(pWnd, nHitTest, message);
2270 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2272 CView::OnKillFocus(pNewWnd);
2273 m_bFocused = FALSE;
2274 UpdateCaret();
2275 Invalidate();
2278 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2280 CView::OnSetFocus(pOldWnd);
2281 m_bFocused = TRUE;
2282 UpdateCaret();
2283 Invalidate();
2286 int CBaseView::GetLineFromPoint(CPoint point)
2288 ScreenToClient(&point);
2289 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2292 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2294 if (!this->IsWindowVisible())
2295 return;
2297 CIconMenu popup;
2298 if (!popup.CreatePopupMenu())
2299 return;
2301 AddContextItems(popup, state);
2303 CMenu popupEols;
2304 CMenu popupUnicode;
2305 int nEncodingCommandBase = POPUPCOMMAND__LAST;
2306 int nEolCommandBase = nEncodingCommandBase+_countof(uctArray);
2307 if (IsWritable())
2309 CString temp;
2310 TWhitecharsProperties oWhites = GetWhitecharsProperties();
2311 temp.LoadString(IDS_EDIT_TAB2SPACE);
2312 popup.AppendMenu(MF_STRING | oWhites.HasTabsToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_TABTOSPACES, temp);
2313 temp.LoadString(IDS_EDIT_SPACE2TAB);
2314 popup.AppendMenu(MF_STRING | oWhites.HasSpacesToConvert ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_SPACESTOTABS, temp);
2315 temp.LoadString(IDS_EDIT_TRIM);
2316 popup.AppendMenu(MF_STRING | oWhites.HasTrailWhiteChars ? MF_ENABLED : (MF_DISABLED|MF_GRAYED), POPUPCOMMAND_REMOVETRAILWHITES, temp);
2318 // add eol submenu
2319 if (!popupEols.CreatePopupMenu())
2320 return;
2322 EOL eEolType = GetLineEndings(oWhites.HasMixedEols);
2323 for (int i = 1; i < _countof(eolArray); i++)
2325 CString temp = GetEolName(eolArray[i]);
2326 bool bChecked = (eEolType == eolArray[i]);
2327 popupEols.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEolCommandBase+i, temp);
2330 temp.LoadString(IDS_VIEWCONTEXTMENU_EOL);
2331 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupEols.GetSafeHmenu(), temp);
2333 // add encoding submenu
2334 if (!popupUnicode.CreatePopupMenu())
2335 return;
2336 for (int i = 0; i < _countof(uctArray); i++)
2338 CString temp = CFileTextLines::GetEncodingName(uctArray[i]);
2339 bool bChecked = (m_texttype == uctArray[i]);
2340 popupUnicode.AppendMenu(MF_STRING | MF_ENABLED | (bChecked ? MF_CHECKED : 0), nEncodingCommandBase+i, temp);
2342 temp.LoadString(IDS_VIEWCONTEXTMENU_ENCODING);
2343 popup.AppendMenuW(MF_POPUP | MF_ENABLED, (UINT_PTR)popupUnicode.GetSafeHmenu(), temp);
2347 CompensateForKeyboard(point);
2349 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2350 ResetUndoStep();
2351 if ((cmd>=nEncodingCommandBase) && (cmd<nEncodingCommandBase+(int)_countof(uctArray)))
2353 SetTextType(uctArray[cmd-nEncodingCommandBase]);
2355 if ((cmd>=nEolCommandBase) && (cmd<nEolCommandBase+(int)_countof(eolArray)))
2357 ReplaceLineEndings(eolArray[cmd-nEolCommandBase]);
2358 SaveUndoStep();
2360 switch (cmd)
2362 // 2-pane view commands; target is right view
2363 case POPUPCOMMAND_USELEFTBLOCK:
2364 m_pwndRight->UseLeftBlock();
2365 break;
2366 case POPUPCOMMAND_USELEFTFILE:
2367 m_pwndRight->UseLeftFile();
2368 break;
2369 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2370 m_pwndRight->UseBothLeftFirst();
2371 break;
2372 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2373 m_pwndRight->UseBothRightFirst();
2374 break;
2375 case POPUPCOMMAND_MARKBLOCK:
2376 m_pwndRight->MarkBlock(true);
2377 break;
2378 case POPUPCOMMAND_UNMARKBLOCK:
2379 m_pwndRight->MarkBlock(false);
2380 break;
2381 case POPUPCOMMAND_USELEFTFILEEXCEPTMARKED:
2382 m_pwndRight->UseLeftFileExceptMarked();
2383 break;
2384 // 2-pane view multiedit commands; target is left view
2385 case POPUPCOMMAND_PREPENDFROMRIGHT:
2386 if (!m_pwndLeft->IsReadonly())
2387 m_pwndLeft->UseBothRightFirst();
2388 break;
2389 case POPUPCOMMAND_REPLACEBYRIGHT:
2390 if (!m_pwndLeft->IsReadonly())
2391 m_pwndLeft->UseRightBlock();
2392 break;
2393 case POPUPCOMMAND_APPENDFROMRIGHT:
2394 if (!m_pwndLeft->IsReadonly())
2395 m_pwndLeft->UseBothLeftFirst();
2396 break;
2397 case POPUPCOMMAND_USERIGHTFILE:
2398 m_pwndLeft->UseRightFile();
2399 break;
2400 // 3-pane view commands; target is bottom view
2401 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2402 m_pwndBottom->UseBothRightFirst();
2403 break;
2404 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2405 m_pwndBottom->UseBothLeftFirst();
2406 break;
2407 case POPUPCOMMAND_USEYOURBLOCK:
2408 m_pwndBottom->UseRightBlock();
2409 break;
2410 case POPUPCOMMAND_USEYOURFILE:
2411 m_pwndBottom->UseRightFile();
2412 break;
2413 case POPUPCOMMAND_USETHEIRBLOCK:
2414 m_pwndBottom->UseLeftBlock();
2415 break;
2416 case POPUPCOMMAND_USETHEIRFILE:
2417 m_pwndBottom->UseLeftFile();
2418 break;
2419 // copy, cut and paste commands
2420 case ID_EDIT_COPY:
2421 OnEditCopy();
2422 break;
2423 case ID_EDIT_CUT:
2424 OnEditCut();
2425 break;
2426 case ID_EDIT_PASTE:
2427 OnEditPaste();
2428 break;
2429 // white chars manipulations
2430 case POPUPCOMMAND_TABTOSPACES:
2431 ConvertTabToSpaces();
2432 break;
2433 case POPUPCOMMAND_SPACESTOTABS:
2434 Tabularize();
2435 break;
2436 case POPUPCOMMAND_REMOVETRAILWHITES:
2437 RemoveTrailWhiteChars();
2438 break;
2439 default:
2440 return;
2441 } // switch (cmd)
2442 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2443 return;
2446 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2448 if (!m_pViewData)
2449 return;
2451 int nViewBlockStart = -1;
2452 int nViewBlockEnd = -1;
2453 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2454 if ((point.x >= 0) && (point.y >= 0))
2456 int nLine = GetLineFromPoint(point)-1;
2457 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2459 int nViewLine = GetViewLineForScreen(nLine);
2460 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2462 ClearSelection(); // Clear text-copy selection
2464 nViewBlockStart = nViewLine;
2465 nViewBlockEnd = nViewLine;
2466 DiffStates state = m_pViewData->GetState(nViewLine);
2467 while (nViewBlockStart > 0)
2469 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2470 if (!LinesInOneChange(-1, state, lineState))
2471 break;
2472 nViewBlockStart--;
2475 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2477 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2478 if (!LinesInOneChange(1, state, lineState))
2479 break;
2480 nViewBlockEnd++;
2483 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2484 UpdateCaretPosition(point);
2489 // FixSelection(); fix selection range
2490 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2491 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2493 DiffStates state = DIFFSTATE_UNKNOWN;
2494 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2496 // find a more 'relevant' state in the selection
2497 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2499 state = m_pViewData->GetState(i);
2500 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2501 break;
2504 OnContextMenu(point, state);
2507 void CBaseView::RefreshViews()
2509 if (m_pwndLeft)
2511 m_pwndLeft->UpdateStatusBar();
2512 m_pwndLeft->Invalidate();
2514 if (m_pwndRight)
2516 m_pwndRight->UpdateStatusBar();
2517 m_pwndRight->Invalidate();
2519 if (m_pwndBottom)
2521 m_pwndBottom->UpdateStatusBar();
2522 m_pwndBottom->Invalidate();
2524 if (m_pwndLocator)
2525 m_pwndLocator->Invalidate();
2528 void CBaseView::GoToFirstDifference()
2530 SetCaretToFirstViewLine();
2531 SelectNextBlock(1, false, false);
2534 void CBaseView::GoToFirstConflict()
2536 SetCaretToFirstViewLine();
2537 SelectNextBlock(1, true, false);
2540 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2542 ClearSelection();
2543 SetupAllSelection(nStart, max(nStart, nEnd));
2545 UpdateCaretPosition(SetupPoint(0, nStart));
2546 Invalidate();
2549 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2551 ClearSelection();
2552 SetupAllViewSelection(nStart, max(nStart, nEnd));
2554 UpdateCaretViewPosition(SetupPoint(0, nStart));
2555 Invalidate();
2558 void CBaseView::SetupAllViewSelection(int start, int end)
2560 SetupViewSelection(m_pwndBottom, start, end);
2561 SetupViewSelection(m_pwndLeft, start, end);
2562 SetupViewSelection(m_pwndRight, start, end);
2565 void CBaseView::SetupAllSelection(int start, int end)
2567 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2570 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2572 void CBaseView::SetupSelection(int start, int end)
2574 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2577 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2579 if (!IsViewGood(view))
2580 return;
2581 view->SetupViewSelection(start, end);
2584 void CBaseView::SetupViewSelection(int start, int end)
2586 // clear text selection before setting line selection ?
2587 m_nSelViewBlockStart = start;
2588 m_nSelViewBlockEnd = end;
2589 Invalidate();
2593 void CBaseView::OnMergePreviousconflict()
2595 SelectNextBlock(-1, true);
2598 void CBaseView::OnMergeNextconflict()
2600 SelectNextBlock(1, true);
2603 void CBaseView::OnMergeNextdifference()
2605 SelectNextBlock(1, false);
2608 void CBaseView::OnMergePreviousdifference()
2610 SelectNextBlock(-1, false);
2613 bool CBaseView::HasNextConflict()
2615 return SelectNextBlock(1, true, true, true);
2618 bool CBaseView::HasPrevConflict()
2620 return SelectNextBlock(-1, true, true, true);
2623 bool CBaseView::HasNextDiff()
2625 return SelectNextBlock(1, false, true, true);
2628 bool CBaseView::HasPrevDiff()
2630 return SelectNextBlock(-1, false, true, true);
2633 bool CBaseView::LinesInOneChange(int direction,
2634 DiffStates initialLineState, DiffStates currentLineState)
2636 // Checks whether all the adjacent lines starting from the initial line
2637 // and up to the current line form the single change
2639 // First of all, if the two lines have identical states, they surely
2640 // belong to one change.
2641 if (initialLineState == currentLineState)
2642 return true;
2644 // Either we move down and initial line state is "added" or "removed" and
2645 // current line state is "empty"...
2646 if (direction > 0)
2648 if (currentLineState == DIFFSTATE_EMPTY)
2650 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2651 return true;
2653 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2654 return true;
2656 // ...or we move up and initial line state is "empty" and current line
2657 // state is "added" or "removed".
2658 if (direction < 0)
2660 if (initialLineState == DIFFSTATE_EMPTY)
2662 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2663 return true;
2665 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2666 return true;
2668 return false;
2671 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2673 if (! m_pViewData)
2674 return false;
2676 const int linesCount = (int)m_Screen2View.size();
2677 if(linesCount == 0)
2678 return false;
2680 int nCenterPos = GetCaretPosition().y;
2681 int nLimit = -1;
2682 if (nDirection > 0)
2683 nLimit = linesCount;
2685 if (nCenterPos >= linesCount)
2686 nCenterPos = linesCount-1;
2688 if (bSkipEndOfCurrentBlock)
2690 // Find end of current block
2691 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2692 while (nCenterPos != nLimit)
2694 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2695 if (!LinesInOneChange(nDirection, state, lineState))
2696 break;
2697 nCenterPos += nDirection;
2701 // Find next diff/conflict block
2702 while (nCenterPos != nLimit)
2704 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2705 if (!bConflict &&
2706 (linestate != DIFFSTATE_NORMAL) &&
2707 (linestate != DIFFSTATE_UNKNOWN))
2709 break;
2711 if (bConflict &&
2712 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2713 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2714 (linestate == DIFFSTATE_CONFLICTED) ||
2715 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2717 break;
2720 nCenterPos += nDirection;
2722 if (nCenterPos == nLimit)
2723 return false;
2724 if (dryrun)
2725 return (nCenterPos != nLimit);
2727 // Find end of new block
2728 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2729 int nBlockEnd = nCenterPos;
2730 const int maxAllowedLine = nLimit-nDirection;
2731 while (nBlockEnd != maxAllowedLine)
2733 const int lineIndex = nBlockEnd + nDirection;
2734 if (lineIndex >= linesCount)
2735 break;
2736 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2737 if (!LinesInOneChange(nDirection, state, lineState))
2738 break;
2739 nBlockEnd += nDirection;
2742 int nTopPos = nCenterPos - (GetScreenLines()/2);
2743 if (nTopPos < 0)
2744 nTopPos = 0;
2746 POINT ptCaretPos = {0, nCenterPos};
2747 SetCaretPosition(ptCaretPos);
2748 ClearSelection();
2749 if (nDirection > 0)
2750 SetupAllSelection(nCenterPos, nBlockEnd);
2751 else
2752 SetupAllSelection(nBlockEnd, nCenterPos);
2754 ScrollAllToLine(nTopPos, FALSE);
2755 RecalcAllVertScrollBars(TRUE);
2756 SetCaretToLineStart();
2757 EnsureCaretVisible();
2758 OnNavigateNextinlinediff();
2760 UpdateViewsCaretPosition();
2761 UpdateCaret();
2762 ShowDiffLines(nCenterPos);
2763 return true;
2766 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2768 if (pNMHDR->idFrom != (UINT)m_hWnd)
2769 return FALSE;
2771 CString strTipText;
2772 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2774 DWORD pos = GetMessagePos();
2775 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2776 ScreenToClient(&point);
2777 const int nLine = GetButtonEventLineIndex(point);
2779 if (nLine >= 0)
2781 int nViewLine = GetViewLineForScreen(nLine);
2782 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2784 auto movedIndex = m_pViewData->GetMovedIndex(nViewLine);
2785 if (movedIndex >= 0)
2787 if (m_pViewData->IsMovedFrom(nViewLine))
2789 strTipText.Format(IDS_MOVED_TO_TT, movedIndex+1);
2791 else
2793 strTipText.Format(IDS_MOVED_FROM_TT, movedIndex+1);
2800 *pResult = 0;
2801 if (strTipText.IsEmpty())
2802 return TRUE;
2804 // need to handle both ANSI and UNICODE versions of the message
2805 if (pNMHDR->code == TTN_NEEDTEXTA)
2807 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2808 pTTTA->lpszText = m_szTip;
2809 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2811 else
2813 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2814 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2815 pTTTW->lpszText = m_wszTip;
2818 return TRUE; // message was handled
2821 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2823 CRect rcClient;
2824 GetClientRect(rcClient);
2825 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2826 int marginwidth = MARGINWIDTH;
2827 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2829 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2831 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2833 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2835 // inside the header part of the view (showing the filename)
2836 pTI->hwnd = this->m_hWnd;
2837 this->GetClientRect(&pTI->rect);
2838 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2839 pTI->uId = (UINT)m_hWnd;
2840 pTI->lpszText = LPSTR_TEXTCALLBACK;
2842 // we want multi line tooltips
2843 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2844 if (pToolTip->GetSafeHwnd() != NULL)
2846 pToolTip->SetMaxTipWidth(INT_MAX);
2849 return (textrect.PtInRect(point) ? 1 : 2);
2852 return -1;
2855 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2857 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2858 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2860 switch (nChar)
2862 case VK_TAB:
2863 if (bControl)
2865 if (this==m_pwndLeft)
2867 if (IsViewGood(m_pwndRight))
2869 m_pwndRight->SetFocus();
2871 else if (IsViewGood(m_pwndBottom))
2873 m_pwndBottom->SetFocus();
2876 else if (this==m_pwndRight)
2878 if (IsViewGood(m_pwndBottom))
2880 m_pwndBottom->SetFocus();
2882 else if (IsViewGood(m_pwndLeft))
2884 m_pwndLeft->SetFocus();
2887 else if (this==m_pwndBottom)
2889 if (IsViewGood(m_pwndLeft))
2891 m_pwndLeft->SetFocus();
2893 else if (IsViewGood(m_pwndRight))
2895 m_pwndRight->SetFocus();
2899 break;
2900 case VK_PRIOR:
2902 POINT ptCaretPos = GetCaretPosition();
2903 ptCaretPos.y -= GetScreenLines();
2904 ptCaretPos.y = max(ptCaretPos.y, 0);
2905 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2906 SetCaretPosition(ptCaretPos);
2907 OnCaretMove(MOVELEFT, bShift);
2908 ShowDiffLines(ptCaretPos.y);
2910 break;
2911 case VK_NEXT:
2913 POINT ptCaretPos = GetCaretPosition();
2914 ptCaretPos.y += GetScreenLines();
2915 if (ptCaretPos.y >= GetLineCount())
2916 ptCaretPos.y = GetLineCount()-1;
2917 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2918 SetCaretPosition(ptCaretPos);
2919 OnCaretMove(MOVERIGHT, bShift);
2920 ShowDiffLines(ptCaretPos.y);
2922 break;
2923 case VK_HOME:
2925 if (bControl)
2927 ScrollAllToLine(0);
2928 SetCaretToViewStart();
2929 m_nCaretGoalPos = 0;
2930 if (bShift)
2931 AdjustSelection(MOVELEFT);
2932 else
2933 ClearSelection();
2934 UpdateCaret();
2936 else
2938 POINT ptCaretPos = GetCaretPosition();
2939 const CString &sLine = GetViewLine(ptCaretPos.y);
2940 int pos = 0;
2941 while (pos < sLine.GetLength())
2943 if (sLine[pos] != ' ' && sLine[pos] != '\t')
2944 break;
2945 ++pos;
2947 if (ptCaretPos.x == pos)
2949 SetCaretToLineStart();
2950 m_nCaretGoalPos = 0;
2951 OnCaretMove(MOVERIGHT, bShift);
2952 ScrollAllToChar(0);
2954 else
2956 ptCaretPos.x = pos;
2957 SetCaretAndGoalPosition(ptCaretPos);
2958 OnCaretMove(bShift);
2962 break;
2963 case VK_END:
2965 if (bControl)
2967 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2968 POINT ptCaretPos;
2969 ptCaretPos.y = GetLineCount()-1;
2970 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2971 SetCaretAndGoalPosition(ptCaretPos);
2972 if (bShift)
2973 AdjustSelection(MOVERIGHT);
2974 else
2975 ClearSelection();
2977 else
2979 POINT ptCaretPos = GetCaretPosition();
2980 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2981 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2983 ptCaretPos.x--;
2985 SetCaretAndGoalPosition(ptCaretPos);
2986 OnCaretMove(bShift);
2989 break;
2990 case VK_BACK:
2991 if (IsWritable())
2993 if (! HasTextSelection())
2995 POINT ptCaretPos = GetCaretPosition();
2996 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2997 break;
2998 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2999 if (bControl)
3000 MoveCaretWordLeft();
3001 else
3003 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
3007 m_ptSelectionViewPosStart = GetCaretViewPosition();
3009 RemoveSelectedText();
3011 break;
3012 case VK_DELETE:
3013 if (IsWritable())
3015 if (! HasTextSelection())
3017 if (bControl)
3019 m_ptSelectionViewPosStart = GetCaretViewPosition();
3020 MoveCaretWordRight();
3021 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3023 else
3025 if (! MoveCaretRight())
3026 break;
3027 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3028 MoveCaretLeft();
3029 m_ptSelectionViewPosStart = GetCaretViewPosition();
3032 RemoveSelectedText();
3034 break;
3035 case VK_INSERT:
3036 m_bInsertMode = !m_bInsertMode;
3037 UpdateCaret();
3038 break;
3040 CView::OnKeyDown(nChar, nRepCnt, nFlags);
3043 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
3045 const int nClickedLine = GetButtonEventLineIndex(point);
3046 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
3048 POINT ptCaretPos;
3049 ptCaretPos.y = nClickedLine;
3050 LONG xpos = point.x - GetMarginWidth();
3051 LONG xpos2 = xpos / GetCharWidth();
3052 xpos2 += m_nOffsetChar;
3053 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
3054 xpos2++;
3055 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
3056 SetCaretAndGoalPosition(ptCaretPos);
3058 if (nFlags & MK_SHIFT)
3059 AdjustSelection(MOVERIGHT);
3060 else
3062 ClearSelection();
3063 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
3064 if (point.x < GetMarginWidth())
3066 // select the whole line
3067 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
3068 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
3072 UpdateViewsCaretPosition();
3073 Invalidate();
3076 CView::OnLButtonDown(nFlags, point);
3079 CBaseView::ECharGroup CBaseView::GetCharGroup(wchar_t zChar) const
3081 if (zChar == ' ' || zChar == '\t' )
3083 return CHG_WHITESPACE;
3085 if (zChar < 0x20)
3087 return CHG_CONTROL;
3089 if (m_sWordSeparators.Find(zChar) >= 0)
3091 return CHG_WORDSEPARATOR;
3093 return CHG_WORDLETTER;
3096 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
3098 if (m_pViewData == 0) {
3099 CView::OnLButtonDblClk(nFlags, point);
3100 return;
3103 const int nClickedLine = GetButtonEventLineIndex(point);
3104 if ( nClickedLine < 0)
3105 return;
3106 int nViewLine = GetViewLineForScreen(nClickedLine);
3107 if (point.x < GetMarginWidth()) // only if double clicked on the margin
3109 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
3111 if (m_pViewData->GetMovedIndex(nViewLine)>=0)
3113 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
3114 int screenLine = FindViewLineNumber(movedindex);
3115 int nTop = screenLine - GetScreenLines()/2;
3116 if (nTop < 0)
3117 nTop = 0;
3118 ScrollAllToLine(nTop);
3119 // find and select the whole moved block
3120 int startSel = movedindex;
3121 int endSel = movedindex;
3122 while ((startSel > 0) && (m_pOtherViewData->GetMovedIndex(startSel) >= 0))
3123 startSel--;
3124 startSel++;
3125 while ((endSel < GetLineCount()) && (m_pOtherViewData->GetMovedIndex(endSel) >= 0))
3126 endSel++;
3127 endSel--;
3128 m_pOtherView->SetupSelection(startSel, endSel);
3129 return CView::OnLButtonDblClk(nFlags, point);
3133 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3135 // a double click on a marker expands the hidden text
3136 int i = nViewLine;
3137 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3139 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3140 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3141 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3142 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3143 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3144 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3145 i++;
3147 BuildAllScreen2ViewVector();
3148 if (m_pwndLeft)
3149 m_pwndLeft->Invalidate();
3150 if (m_pwndRight)
3151 m_pwndRight->Invalidate();
3152 if (m_pwndBottom)
3153 m_pwndBottom->Invalidate();
3155 else
3157 POINT ptCaretPos;
3158 ptCaretPos.y = nClickedLine;
3159 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3160 SetCaretPosition(ptCaretPos);
3161 ClearSelection();
3163 POINT ptViewCarret = GetCaretViewPosition();
3164 nViewLine = ptViewCarret.y;
3165 if (nViewLine >= GetViewCount())
3166 return;
3167 const CString &sLine = GetViewLine(nViewLine);
3168 int nLineLength = sLine.GetLength();
3169 int nBasePos = ptViewCarret.x;
3170 // get target char group
3171 ECharGroup eLeft = CHG_UNKNOWN;
3172 if (nBasePos > 0)
3174 eLeft = GetCharGroup(sLine[nBasePos-1]);
3176 ECharGroup eRight = CHG_UNKNOWN;
3177 if (nBasePos < nLineLength)
3179 eRight = GetCharGroup(sLine[nBasePos]);
3181 ECharGroup eTarget = max(eRight, eLeft);
3182 // find left margin
3183 int nLeft = nBasePos;
3184 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3186 nLeft--;
3188 // get right margin
3189 int nRight = nBasePos;
3190 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3192 nRight++;
3194 // set selection
3195 m_ptSelectionViewPosStart.x = nLeft;
3196 m_ptSelectionViewPosStart.y = nViewLine;
3197 m_ptSelectionViewPosEnd.x = nRight;
3198 m_ptSelectionViewPosEnd.y = nViewLine;
3199 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3200 SetupAllViewSelection(nViewLine, nViewLine);
3201 // set caret
3202 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3203 UpdateViewsCaretPosition();
3204 UpdateGoalPos();
3206 // set mark word
3207 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3208 int nMarkWidth = max(nRight - nLeft, 0);
3209 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3210 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3212 m_sMarkedWord.Empty();
3215 if (m_pwndLeft)
3216 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3217 if (m_pwndRight)
3218 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3219 if (m_pwndBottom)
3220 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3222 Invalidate();
3223 if (m_pwndLocator)
3224 m_pwndLocator->Invalidate();
3227 CView::OnLButtonDblClk(nFlags, point);
3230 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3232 const int nClickedLine = GetButtonEventLineIndex(point);
3233 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3235 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3237 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3238 if (pidl)
3240 SHOpenFolderAndSelectItems(pidl,0,0,0);
3241 CoTaskMemFree((LPVOID)pidl);
3244 return;
3246 POINT ptCaretPos;
3247 ptCaretPos.y = nClickedLine;
3248 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3249 SetCaretAndGoalPosition(ptCaretPos);
3250 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3251 if (m_pwndLeft)
3252 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3253 if (m_pwndRight)
3254 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3255 if (m_pwndBottom)
3256 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3257 ClearSelection();
3258 m_ptSelectionViewPosStart.x = 0;
3259 m_ptSelectionViewPosStart.y = nClickedLine;
3260 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3261 m_ptSelectionViewPosEnd.y = nClickedLine;
3262 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3263 UpdateViewsCaretPosition();
3264 Invalidate();
3265 if (m_pwndLocator)
3266 m_pwndLocator->Invalidate();
3269 void CBaseView::OnEditCopy()
3271 CString sCopyData = GetSelectedText();
3273 if (!sCopyData.IsEmpty())
3275 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3279 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3281 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3283 --m_pMainFrame->m_nMoveMovesToIgnore;
3284 CView::OnMouseMove(nFlags, point);
3285 return;
3287 int nMouseLine = GetButtonEventLineIndex(point);
3288 if (nMouseLine < -1)
3289 nMouseLine = -1;
3290 m_mouseInMargin = point.x < GetMarginWidth();
3292 ShowDiffLines(nMouseLine);
3294 KillTimer(IDT_SCROLLTIMER);
3295 if (nFlags & MK_LBUTTON)
3297 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3298 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3299 if (saveMouseLine < 0)
3300 return;
3301 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3302 if (HasSelection() &&
3303 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3305 POINT ptCaretPos = {charIndex, nMouseLine};
3306 SetCaretAndGoalPosition(ptCaretPos);
3307 AdjustSelection(MOVERIGHT);
3308 Invalidate();
3309 UpdateWindow();
3311 if (nMouseLine < m_nTopLine)
3313 ScrollAllToLine(m_nTopLine-1, TRUE);
3314 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3316 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3318 ScrollAllToLine(m_nTopLine+1, TRUE);
3319 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3321 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3323 ScrollAllSide(-1);
3324 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3326 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3328 ScrollAllSide(1);
3329 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3331 SetCapture();
3335 CView::OnMouseMove(nFlags, point);
3338 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3340 ShowDiffLines(-1);
3341 ReleaseCapture();
3342 KillTimer(IDT_SCROLLTIMER);
3344 __super::OnLButtonUp(nFlags, point);
3347 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3349 if (nIDEvent == IDT_SCROLLTIMER)
3351 POINT point;
3352 GetCursorPos(&point);
3353 ScreenToClient(&point);
3354 int nMouseLine = GetButtonEventLineIndex(point);
3355 if (nMouseLine < -1)
3357 nMouseLine = -1;
3359 if (GetKeyState(VK_LBUTTON)&0x8000)
3361 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3362 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3363 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3364 if (nMouseLine < m_nTopLine)
3366 ScrollAllToLine(m_nTopLine-1, TRUE);
3367 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3369 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3371 ScrollAllToLine(m_nTopLine+1, TRUE);
3372 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3374 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3376 ScrollAllSide(-1);
3377 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3379 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3381 ScrollAllSide(1);
3382 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3388 CView::OnTimer(nIDEvent);
3391 void CBaseView::ShowDiffLines(int nLine)
3393 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3395 m_pwndLineDiffBar->ShowLines(nLine);
3396 nLine = -1;
3397 m_nMouseLine = nLine;
3398 return;
3401 if ((!m_pwndRight)||(!m_pwndLeft))
3402 return;
3403 if(m_pMainFrame->m_bOneWay)
3404 return;
3406 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3407 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3409 if (nLine < 0)
3410 return;
3412 if (nLine != m_nMouseLine)
3414 if (nLine >= GetLineCount())
3415 nLine = -1;
3416 m_nMouseLine = nLine;
3417 m_pwndLineDiffBar->ShowLines(nLine);
3419 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3422 const viewdata& CBaseView::GetEmptyLineData()
3424 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN);
3425 return emptyLine;
3428 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3430 for (int i = 0; i < nCount; i++)
3432 InsertViewData(nFirstView, GetEmptyLineData());
3437 void CBaseView::UpdateCaret()
3439 POINT ptCaretPos = GetCaretPosition();
3440 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3441 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3442 SetCaretPosition(ptCaretPos);
3444 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3446 if (m_bFocused &&
3447 ptCaretPos.y >= m_nTopLine &&
3448 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3449 nCaretOffset >= m_nOffsetChar &&
3450 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3452 POINT pt1 = TextToClient(ptCaretPos);
3453 if (m_bInsertMode)
3454 CreateSolidCaret(2, GetLineHeight());
3455 else
3457 POINT pt = { ptCaretPos.x + 1, ptCaretPos.y };
3458 POINT pt2 = TextToClient(pt);
3459 int width = max(GetCharWidth(), pt2.x - pt1.x);
3460 CreateSolidCaret(width, GetLineHeight());
3462 SetCaretPos(pt1);
3463 ShowCaret();
3465 else
3467 HideCaret();
3471 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3473 POINT ptViewPos;
3474 ptViewPos.x = pt.x;
3476 int nSubLine = GetSubLineOffset(pt.y);
3477 if (nSubLine > 0)
3479 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3481 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3485 ptViewPos.y = GetViewLineForScreen(pt.y);
3486 return ptViewPos;
3489 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3491 POINT ptPos;
3492 int nViewLineLenLeft = GetViewLineLength(pt.y);
3493 ptPos.x = min(nViewLineLenLeft, pt.x);
3494 ptPos.y = FindScreenLineForViewLine(pt.y);
3495 if (GetViewLineForScreen(ptPos.y) != pt.y )
3497 ptPos.x = 0;
3499 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3501 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3502 while (nSubLineLength < ptPos.x)
3504 ptPos.x -= nSubLineLength;
3505 nViewLineLenLeft -= nSubLineLength;
3506 ptPos.y++;
3507 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3509 // last pos of non last sub-line go to start of next screen line
3510 // Note: while this works correctly, it's not what a user might expect:
3511 // cursor-right when the caret is before the last char of a wrapped line
3512 // now moves the caret to the next line. But users expect the caret to
3513 // move to the right of the last char instead, and with another cursor-right
3514 // keystroke to move the caret to the next line.
3515 // Basically, this would require to handle two caret positions for the same
3516 // logical position in the line string (one on the last position of the first line,
3517 // one on the first position of the new line. For non-wrapped lines this works
3518 // because there's an 'invisible' newline char at the end of the first line.
3519 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3521 ptPos.x = 0;
3522 ptPos.y++;
3526 return ptPos;
3530 void CBaseView::EnsureCaretVisible()
3532 POINT ptCaretPos = GetCaretPosition();
3533 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3535 if (ptCaretPos.y < m_nTopLine)
3536 ScrollAllToLine(ptCaretPos.y);
3537 int screnLines = GetScreenLines();
3538 if (screnLines)
3540 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3541 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3542 if (nCaretOffset < m_nOffsetChar)
3543 ScrollAllToChar(nCaretOffset);
3544 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3545 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3549 int CBaseView::CalculateActualOffset(const POINT& point)
3551 int nLineIndex = point.y;
3552 int nCharIndex = point.x;
3553 ASSERT(nCharIndex >= 0);
3554 CString sLine = GetLineChars(nLineIndex);
3555 int nLineLength = sLine.GetLength();
3556 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3559 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3561 int nLength = GetLineLength(nLineIndex);
3562 int nSubLine = GetSubLineOffset(nLineIndex);
3563 if (nSubLine>=0)
3565 int nViewLine = GetViewLineForScreen(nLineIndex);
3566 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3568 int nMultilineCount = CountMultiLines(nViewLine);
3569 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3571 nLength--;
3575 CString Line = GetLineChars(nLineIndex);
3576 int nIndex = 0;
3577 int nOffset = 0;
3578 int nTabSize = GetTabSize();
3579 while (nOffset < nActualOffset && nIndex < nLength)
3581 if (Line.GetAt(nIndex) == _T('\t'))
3582 nOffset += (nTabSize - nOffset % nTabSize);
3583 else
3584 ++nOffset;
3585 ++nIndex;
3587 return nIndex;
3590 POINT CBaseView::TextToClient(const POINT& point)
3592 POINT pt;
3593 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3594 pt.y = nOffsetScreenLine * GetLineHeight();
3595 pt.x = CalculateActualOffset(point);
3597 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3598 CDC * pDC = GetDC();
3599 if (pDC)
3601 pDC->SelectObject(GetFont()); // is this right font ?
3602 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3603 CString sLine = GetLineChars(nScreenLine);
3604 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3605 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3606 ReleaseDC(pDC);
3607 } else {
3608 nLeft += pt.x * GetCharWidth();
3611 pt.x = nLeft;
3612 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3613 return pt;
3616 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3618 CView::OnChar(nChar, nRepCnt, nFlags);
3620 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
3621 bool bSkipSelectionClear = false;
3623 if (IsReadonly())
3624 return;
3626 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3627 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3629 return;
3632 if (!m_pViewData) // no data - nothing to do
3633 return;
3635 if (nChar == VK_F16)
3637 // generated by a ctrl+backspace - ignore.
3639 else if (nChar==VK_TAB && HasTextLineSelection())
3641 // change indentation for selected lines
3642 if (bShift)
3644 RemoveIndentationForSelectedBlock();
3646 else
3648 AddIndentationForSelectedBlock();
3650 bSkipSelectionClear = true;
3652 else if ((nChar > 31)||(nChar == VK_TAB))
3654 ResetUndoStep();
3655 RemoveSelectedText();
3656 POINT ptCaretViewPos = GetCaretViewPosition();
3657 int nViewLine = ptCaretViewPos.y;
3658 if ((nViewLine==0)&&(GetViewCount()==0))
3659 OnChar(VK_RETURN, 0, 0);
3660 int charCount = 1;
3661 viewdata lineData = GetViewData(nViewLine);
3662 if (nChar == VK_TAB)
3664 int indentChars = GetIndentCharsForLine(ptCaretViewPos.x, nViewLine);
3665 if (indentChars > 0)
3667 lineData.sLine.Insert(ptCaretViewPos.x, CString(_T(' '), indentChars));
3668 charCount = indentChars;
3670 else
3671 lineData.sLine.Insert(ptCaretViewPos.x, _T('\t'));
3673 else
3675 if (m_bInsertMode)
3676 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3677 else
3679 if (lineData.sLine.GetLength() > ptCaretViewPos.x)
3680 lineData.sLine.SetAt(ptCaretViewPos.x, (wchar_t)nChar);
3681 else
3682 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3685 if (IsStateEmpty(lineData.state))
3687 // if not last line set EOL
3688 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3690 if (!IsViewLineEmpty(nCheckViewLine))
3692 lineData.ending = m_lineendings;
3693 break;
3696 // make sure previous (non empty) line have EOL set
3697 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3699 if (!IsViewLineEmpty(nCheckViewLine))
3701 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3703 SetViewLineEnding(nCheckViewLine, m_lineendings);
3705 break;
3709 lineData.state = DIFFSTATE_EDITED;
3710 bool bNeedRenumber = false;
3711 if (lineData.linenumber == -1)
3713 lineData.linenumber = 0;
3714 bNeedRenumber = true;
3716 SetViewData(nViewLine, lineData);
3717 SetModified();
3718 SaveUndoStep();
3719 BuildAllScreen2ViewVector(nViewLine);
3720 if (bNeedRenumber)
3722 UpdateViewLineNumbers();
3724 for (int i = 0; i < charCount; ++i)
3725 MoveCaretRight();
3726 UpdateGoalPos();
3728 else if (nChar == 10)
3730 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3731 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3732 EOL newEOL = EOL_CRLF;
3733 switch (eol)
3735 case EOL_CRLF:
3736 newEOL = EOL_CR;
3737 break;
3738 case EOL_CR:
3739 newEOL = EOL_LF;
3740 break;
3741 case EOL_LF:
3742 newEOL = EOL_CRLF;
3743 break;
3745 if (eol==EOL_NOENDING || eol==newEOL)
3746 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3747 // to add EOL on newly edited empty line hit enter
3748 // don't store into UNDO if no change happened
3749 // and don't mark file as modified
3750 return;
3751 AddUndoViewLine(nViewLine);
3752 m_pViewData->SetLineEnding(nViewLine, newEOL);
3753 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3754 UpdateGoalPos();
3756 else if (nChar == VK_RETURN)
3758 // insert a new, fresh and empty line below the cursor
3759 RemoveSelectedText();
3761 CUndo::GetInstance().BeginGrouping();
3763 POINT ptCaretViewPos = GetCaretViewPosition();
3764 int nViewLine = ptCaretViewPos.y;
3765 int nLeft = ptCaretViewPos.x;
3766 CString sLine = GetViewLineChars(nViewLine);
3767 CString sLineLeft = sLine.Left(nLeft);
3768 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3769 EOL eOriginalEnding = EOL_AUTOLINE;
3770 if (m_pViewData->GetCount() > nViewLine)
3771 eOriginalEnding = GetViewLineEnding(nViewLine);
3773 if (!sLineRight.IsEmpty() || (eOriginalEnding!=m_lineendings))
3775 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
3776 SetViewData(nViewLine, newFirstLine);
3779 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3780 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN);
3781 InsertViewData(nInsertLine, newLastLine);
3782 SetModified();
3783 SaveUndoStep();
3785 // adds new line everywhere except me
3786 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3788 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3790 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3792 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3794 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3796 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3798 SaveUndoStep();
3800 UpdateViewLineNumbers();
3801 SaveUndoStep();
3802 CUndo::GetInstance().EndGrouping();
3804 BuildAllScreen2ViewVector();
3805 // move the cursor to the new line
3806 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3807 SetCaretAndGoalViewPosition(ptCaretViewPos);
3809 else
3810 return; // Unknown control character -- ignore it.
3811 if (!bSkipSelectionClear)
3812 ClearSelection();
3813 EnsureCaretVisible();
3814 UpdateCaret();
3815 Invalidate(FALSE);
3818 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3820 ResetUndoStep();
3821 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3822 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3823 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3824 SetModified();
3825 SaveUndoStep();
3826 RecalcAllVertScrollBars();
3827 Invalidate(FALSE);
3830 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3832 if (m_pViewData == NULL)
3833 return;
3834 int viewLine = nViewLineIndex;
3835 EOL ending = m_pViewData->GetLineEnding(viewLine);
3836 if (ending == EOL_NOENDING)
3838 ending = m_lineendings;
3840 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN);
3841 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3843 CString sPartLine = GetViewLineChars(nViewLineIndex);
3844 int nPosx = GetCaretPosition().x; // should be view pos ?
3845 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3846 sPartLine = sPartLine.Mid(nPosx);
3847 newLine.sLine = sPartLine;
3849 m_pViewData->InsertData(viewLine+1, newLine);
3850 BuildAllScreen2ViewVector();
3853 void CBaseView::RemoveSelectedText()
3855 if (m_pViewData == NULL)
3856 return;
3857 if (!HasTextSelection())
3858 return;
3860 // fix selection if starts or ends on empty line
3861 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3862 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3865 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3866 SetCaretViewPosition(m_ptSelectionViewPosStart);
3867 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3870 m_ptSelectionViewPosStart = GetCaretViewPosition();
3871 if (!HasTextSelection())
3873 ClearSelection();
3874 return;
3877 // We want to undo the insertion in a single step.
3878 ResetUndoStep();
3879 CUndo::GetInstance().BeginGrouping();
3881 // combine first and last line
3882 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3883 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3884 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3885 oFirstLine.ending = oLastLine.ending;
3886 oFirstLine.state = DIFFSTATE_EDITED;
3887 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3889 // clean up middle lines if any
3890 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3892 viewdata oEmptyLine = GetEmptyLineData();
3893 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3895 SetViewData(nViewLine, oEmptyLine);
3897 SaveUndoStep();
3899 if (CleanEmptyLines())
3901 BuildAllScreen2ViewVector(); // schedule full rebuild
3903 SaveUndoStep();
3904 UpdateViewLineNumbers();
3907 SetModified(); //TODO set modified only if real data was changed
3908 SaveUndoStep();
3909 CUndo::GetInstance().EndGrouping();
3911 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3912 SetCaretViewPosition(m_ptSelectionViewPosStart);
3913 UpdateGoalPos();
3914 ClearSelection();
3915 UpdateCaret();
3916 EnsureCaretVisible();
3917 Invalidate(FALSE);
3920 void CBaseView::PasteText()
3922 if (!OpenClipboard())
3923 return;
3925 CString sClipboardText;
3926 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3927 if (hglb)
3929 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3930 sClipboardText = CString(lpstr);
3931 GlobalUnlock(hglb);
3933 hglb = GetClipboardData(CF_UNICODETEXT);
3934 if (hglb)
3936 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3937 sClipboardText = lpstr;
3938 GlobalUnlock(hglb);
3940 CloseClipboard();
3942 if (sClipboardText.IsEmpty())
3943 return;
3945 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3946 sClipboardText.Replace('\n', '\r');
3948 InsertText(sClipboardText);
3951 void CBaseView::OnCaretDown()
3953 POINT ptCaretPos = GetCaretPosition();
3954 int nLine = ptCaretPos.y;
3955 int nNextLine = nLine + 1;
3956 if (nNextLine >= GetLineCount()) // already at last line
3958 return;
3961 POINT ptCaretViewPos = GetCaretViewPosition();
3962 int nViewLine = ptCaretViewPos.y;
3963 int nNextViewLine = GetViewLineForScreen(nNextLine);
3964 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3966 // find next suitable screen line
3967 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3969 nNextLine++;
3970 if (nNextLine >= GetLineCount())
3972 return;
3974 nNextViewLine = GetViewLineForScreen(nNextLine);
3977 ptCaretPos.y = nNextLine;
3978 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3979 SetCaretPosition(ptCaretPos);
3980 OnCaretMove(MOVELEFT);
3981 ShowDiffLines(ptCaretPos.y);
3984 bool CBaseView::MoveCaretLeft()
3986 POINT ptCaretViewPos = GetCaretViewPosition();
3988 //int nViewLine = ptCaretViewPos.y;
3989 if (ptCaretViewPos.x == 0)
3991 int nPrevLine = GetCaretPosition().y;
3992 int nPrevViewLine;
3993 do {
3994 nPrevLine--;
3995 if (nPrevLine < 0)
3997 return false;
3999 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4000 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
4001 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
4002 ShowDiffLines(nPrevLine);
4004 else
4005 --ptCaretViewPos.x;
4007 SetCaretAndGoalViewPosition(ptCaretViewPos);
4008 return true;
4011 bool CBaseView::MoveCaretRight()
4013 POINT ptCaretViewPos = GetCaretViewPosition();
4015 int nViewLine = ptCaretViewPos.y;
4016 int nViewLineLen = GetViewLineLength(nViewLine);
4017 if (ptCaretViewPos.x >= nViewLineLen)
4019 int nNextLine = GetCaretPosition().y;
4020 int nNextViewLine;
4021 do {
4022 nNextLine++;
4023 if (nNextLine >= GetLineCount())
4025 return false;
4027 nNextViewLine = GetViewLineForScreen(nNextLine);
4028 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
4029 ptCaretViewPos.y = nNextViewLine;
4030 ptCaretViewPos.x = 0;
4031 ShowDiffLines(nNextLine);
4033 else
4034 ++ptCaretViewPos.x;
4036 SetCaretAndGoalViewPosition(ptCaretViewPos);
4037 return true;
4040 void CBaseView::UpdateGoalPos()
4042 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
4045 void CBaseView::OnCaretLeft()
4047 MoveCaretLeft();
4048 OnCaretMove(MOVELEFT);
4051 void CBaseView::OnCaretRight()
4053 MoveCaretRight();
4054 OnCaretMove(MOVERIGHT);
4057 void CBaseView::OnCaretUp()
4059 POINT ptCaretPos = GetCaretPosition();
4060 int nLine = ptCaretPos.y;
4061 if (nLine <= 0) // already at first line
4063 return;
4065 int nPrevLine = nLine - 1;
4067 POINT ptCaretViewPos = GetCaretViewPosition();
4068 int nViewLine = ptCaretViewPos.y;
4069 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
4070 if (nPrevViewLine != nViewLine) // not on same view line
4072 // find previous suitable screen line
4073 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
4075 if (nPrevLine <= 0)
4077 return;
4079 nPrevLine--;
4080 nPrevViewLine = GetViewLineForScreen(nPrevLine);
4083 ptCaretPos.y = nPrevLine;
4084 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
4085 SetCaretPosition(ptCaretPos);
4086 OnCaretMove(MOVELEFT);
4087 ShowDiffLines(ptCaretPos.y);
4090 bool CBaseView::IsWordSeparator(const wchar_t ch) const
4092 switch (GetCharGroup(ch))
4094 case CHG_CONTROL:
4095 case CHG_WHITESPACE:
4096 case CHG_WORDSEPARATOR:
4097 return true;
4099 return false;
4102 bool CBaseView::IsCaretAtWordBoundary()
4104 POINT ptViewCaret = GetCaretViewPosition();
4105 CString line = GetViewLineChars(ptViewCaret.y);
4106 if (line.IsEmpty())
4107 return false; // no boundary at the empty lines
4108 if (ptViewCaret.x == 0)
4109 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4110 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4111 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4112 return
4113 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4114 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4117 void CBaseView::UpdateViewsCaretPosition()
4119 POINT ptCaretPos = GetCaretPosition();
4120 if (m_pwndBottom && m_pwndBottom!=this)
4121 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4122 if (m_pwndLeft && m_pwndLeft!=this)
4123 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4124 if (m_pwndRight && m_pwndRight!=this)
4125 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4128 void CBaseView::OnCaretWordleft()
4130 MoveCaretWordLeft();
4131 OnCaretMove(MOVELEFT);
4134 void CBaseView::OnCaretWordright()
4136 MoveCaretWordRight();
4137 OnCaretMove(MOVERIGHT);
4140 void CBaseView::MoveCaretWordLeft()
4142 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4147 void CBaseView::MoveCaretWordRight()
4149 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4154 void CBaseView::ClearCurrentSelection()
4156 m_ptSelectionViewPosStart = GetCaretViewPosition();
4157 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4158 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4159 m_nSelViewBlockStart = -1;
4160 m_nSelViewBlockEnd = -1;
4161 Invalidate(FALSE);
4164 void CBaseView::ClearSelection()
4166 if (m_pwndLeft)
4167 m_pwndLeft->ClearCurrentSelection();
4168 if (m_pwndRight)
4169 m_pwndRight->ClearCurrentSelection();
4170 if (m_pwndBottom)
4171 m_pwndBottom->ClearCurrentSelection();
4174 void CBaseView::AdjustSelection(bool bMoveLeft)
4176 POINT ptCaretViewPos = GetCaretViewPosition();
4177 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4179 // select all have been used recently update origin
4180 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4182 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4183 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4185 m_ptSelectionViewPosStart = ptCaretViewPos;
4186 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4188 else
4190 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4191 m_ptSelectionViewPosEnd = ptCaretViewPos;
4194 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4196 Invalidate(FALSE);
4199 void CBaseView::OnEditCut()
4201 if (IsWritable())
4203 OnEditCopy();
4204 RemoveSelectedText();
4208 void CBaseView::OnEditPaste()
4210 if (IsWritable())
4212 CUndo::GetInstance().BeginGrouping();
4213 RemoveSelectedText();
4214 PasteText();
4215 CUndo::GetInstance().EndGrouping();
4219 void CBaseView::DeleteFonts()
4221 for (int i=0; i<fontsCount; i++)
4223 if (m_apFonts[i] != NULL)
4225 m_apFonts[i]->DeleteObject();
4226 delete m_apFonts[i];
4227 m_apFonts[i] = NULL;
4232 void CBaseView::OnCaretMove(bool bMoveLeft)
4234 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4235 OnCaretMove(bMoveLeft, bShift);
4238 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4240 if(isShiftPressed)
4241 AdjustSelection(bMoveLeft);
4242 else
4243 ClearSelection();
4244 EnsureCaretVisible();
4245 UpdateCaret();
4248 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4250 AddCutCopyAndPaste(popup);
4253 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4255 popup.AppendMenu(MF_SEPARATOR, NULL);
4256 CString temp;
4257 temp.LoadString(IDS_EDIT_COPY);
4258 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4259 if (IsWritable())
4261 temp.LoadString(IDS_EDIT_CUT);
4262 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4263 temp.LoadString(IDS_EDIT_PASTE);
4264 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4265 popup.AppendMenu(MF_SEPARATOR, NULL);
4269 void CBaseView::CompensateForKeyboard(CPoint& point)
4271 // if the context menu is invoked through the keyboard, we have to use
4272 // a calculated position on where to anchor the menu on
4273 if (ArePointsSame(point, SetupPoint(-1, -1)))
4275 CRect rect;
4276 GetWindowRect(&rect);
4277 point = rect.CenterPoint();
4281 HICON CBaseView::LoadIcon(WORD iconId)
4283 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4284 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4285 return (HICON)icon;
4288 void CBaseView::ReleaseBitmap()
4290 if (m_pCacheBitmap != NULL)
4292 m_pCacheBitmap->DeleteObject();
4293 delete m_pCacheBitmap;
4294 m_pCacheBitmap = NULL;
4298 void CBaseView::BuildMarkedWordArray()
4300 int lineCount = GetLineCount();
4301 m_arMarkedWordLines.clear();
4302 m_arMarkedWordLines.reserve(lineCount);
4303 bool bDoit = !m_sMarkedWord.IsEmpty();
4304 for (int i = 0; i < lineCount; ++i)
4306 if (bDoit)
4308 CString line = GetLineChars(i);
4310 if (!line.IsEmpty())
4312 int found = 0;
4313 int nMarkStart = -1;
4314 while ((nMarkStart = line.Find(m_sMarkedWord, ++nMarkStart)) >= 0)
4316 int nMarkEnd = nMarkStart + m_sMarkedWord.GetLength();
4317 ECharGroup eLeft = GetCharGroup(line, nMarkStart - 1);
4318 ECharGroup eStart = GetCharGroup(line, nMarkStart);
4319 if (eLeft != eStart)
4321 ECharGroup eRight = GetCharGroup(line, nMarkEnd);
4322 ECharGroup eEnd = GetCharGroup(line, nMarkEnd - 1);
4323 if (eRight != eEnd)
4325 found = 1;
4326 break;
4330 m_arMarkedWordLines.push_back(found);
4332 else
4333 m_arMarkedWordLines.push_back(0);
4335 else
4336 m_arMarkedWordLines.push_back(0);
4340 void CBaseView::BuildFindStringArray()
4342 int lineCount = GetLineCount();
4343 m_arFindStringLines.clear();
4344 m_arFindStringLines.reserve(lineCount);
4345 bool bDoit = !m_sFindText.IsEmpty();
4346 int s = 0;
4347 int e = 0;
4348 for (int i = 0; i < lineCount; ++i)
4350 if (bDoit)
4352 CString line = GetLineChars(i);
4354 if (!line.IsEmpty())
4356 switch (m_pViewData->GetState(i))
4358 case DIFFSTATE_EMPTY:
4359 m_arFindStringLines.push_back(0);
4360 break;
4361 case DIFFSTATE_UNKNOWN:
4362 case DIFFSTATE_NORMAL:
4363 if (m_bLimitToDiff)
4365 m_arFindStringLines.push_back(0);
4366 break;
4368 case DIFFSTATE_REMOVED:
4369 case DIFFSTATE_REMOVEDWHITESPACE:
4370 case DIFFSTATE_ADDED:
4371 case DIFFSTATE_ADDEDWHITESPACE:
4372 case DIFFSTATE_WHITESPACE:
4373 case DIFFSTATE_WHITESPACE_DIFF:
4374 case DIFFSTATE_CONFLICTED:
4375 case DIFFSTATE_CONFLICTED_IGNORED:
4376 case DIFFSTATE_CONFLICTADDED:
4377 case DIFFSTATE_CONFLICTEMPTY:
4378 case DIFFSTATE_CONFLICTRESOLVED:
4379 case DIFFSTATE_IDENTICALREMOVED:
4380 case DIFFSTATE_IDENTICALADDED:
4381 case DIFFSTATE_THEIRSREMOVED:
4382 case DIFFSTATE_THEIRSADDED:
4383 case DIFFSTATE_YOURSREMOVED:
4384 case DIFFSTATE_YOURSADDED:
4385 case DIFFSTATE_EDITED:
4387 if (!m_bMatchCase)
4388 line = line.MakeLower();
4389 s = 0;
4390 e = 0;
4391 int match = 0;
4392 while (StringFound(line, SearchNext, s, e))
4394 match++;
4395 s = e;
4396 e = 0;
4398 m_arFindStringLines.push_back(match);
4399 break;
4401 default:
4402 m_arFindStringLines.push_back(0);
4405 else
4406 m_arFindStringLines.push_back(0);
4408 else
4409 m_arFindStringLines.push_back(0);
4411 UpdateLocator();
4414 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4416 if (!m_bShowInlineDiff)
4417 return false;
4418 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4419 return false;
4421 CString sLine = GetViewLineChars(nViewLine);
4422 if (sLine.IsEmpty())
4423 return false;
4425 CheckOtherView();
4426 if (!m_pOtherViewData)
4427 return false;
4429 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4430 if (sDiffLine.IsEmpty())
4431 return false;
4433 CString sLineExp = ExpandChars(sLine);
4434 CString sDiffLineExp = ExpandChars(sDiffLine);
4435 svn_diff_t * diff = NULL;
4436 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4437 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4438 return false;
4440 size_t lineoffset = 0;
4441 size_t position = 0;
4442 while (diff)
4444 apr_off_t len = diff->original_length;
4445 size_t oldpos = position;
4447 for (apr_off_t i = 0; i < len; ++i)
4449 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4450 lineoffset++;
4453 if (diff->type == svn_diff__type_diff_modified)
4455 inlineDiffPos p;
4456 p.start = oldpos;
4457 p.end = position;
4458 positions.push_back(p);
4461 diff = diff->next;
4464 return !positions.empty();
4467 void CBaseView::OnNavigateNextinlinediff()
4469 int nX;
4470 if (GetNextInlineDiff(nX))
4472 POINT ptCaretViewPos = GetCaretViewPosition();
4473 ptCaretViewPos.x = nX;
4474 SetCaretAndGoalViewPosition(ptCaretViewPos);
4475 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4476 EnsureCaretVisible();
4480 void CBaseView::OnNavigatePrevinlinediff()
4482 int nX;
4483 if (GetPrevInlineDiff(nX))
4485 POINT ptCaretViewPos = GetCaretViewPosition();
4486 ptCaretViewPos.x = nX;
4487 SetCaretAndGoalViewPosition(ptCaretViewPos);
4488 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4489 EnsureCaretVisible();
4493 bool CBaseView::HasNextInlineDiff()
4495 int nPos;
4496 return GetNextInlineDiff(nPos);
4499 bool CBaseView::GetNextInlineDiff(int & nPos)
4501 POINT ptCaretViewPos = GetCaretViewPosition();
4502 std::vector<inlineDiffPos> positions;
4503 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4505 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4507 if (it->start > ptCaretViewPos.x)
4509 nPos = (LONG)it->start;
4510 return true;
4512 if (it->end > ptCaretViewPos.x)
4514 nPos = (LONG)it->end;
4515 return true;
4519 return false;
4522 bool CBaseView::HasPrevInlineDiff()
4524 int nPos;
4525 return GetPrevInlineDiff(nPos);
4528 bool CBaseView::GetPrevInlineDiff(int & nPos)
4530 POINT ptCaretViewPos = GetCaretViewPosition();
4531 std::vector<inlineDiffPos> positions;
4532 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4534 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4536 if ( it->end < ptCaretViewPos.x)
4538 nPos = (LONG)it->end;
4539 return true;
4541 if ( it->start < ptCaretViewPos.x)
4543 nPos = (LONG)it->start;
4544 return true;
4548 return false;
4551 CBaseView * CBaseView::GetFirstGoodView()
4553 if (IsViewGood(m_pwndLeft))
4554 return m_pwndLeft;
4555 if (IsViewGood(m_pwndRight))
4556 return m_pwndRight;
4557 if (IsViewGood(m_pwndBottom))
4558 return m_pwndBottom;
4559 return NULL;
4562 void CBaseView::BuildAllScreen2ViewVector()
4564 CBaseView * p_pwndView = GetFirstGoodView();
4565 if (p_pwndView)
4567 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4571 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4573 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4576 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4578 CBaseView * p_pwndView = GetFirstGoodView();
4579 if (p_pwndView)
4581 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4585 void CBaseView::UpdateViewLineNumbers()
4587 int nLineNumber = 0;
4588 int nViewLineCount = GetViewCount();
4589 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4591 int oldLine = (int)GetViewLineNumber(nViewLine);
4592 if (oldLine >= 0)
4593 SetViewLineNumber(nViewLine, nLineNumber++);
4595 m_nDigits = 0;
4598 int CBaseView::CleanEmptyLines()
4600 int nRemovedCount = 0;
4601 int nViewLineCount = GetViewCount();
4602 bool bCheckLeft = IsViewGood(m_pwndLeft);
4603 bool bCheckRight = IsViewGood(m_pwndRight);
4604 bool bCheckBottom = IsViewGood(m_pwndBottom);
4605 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4607 bool bAllEmpty = true;
4608 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4609 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4610 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4611 if (bAllEmpty)
4613 if (bCheckLeft)
4615 m_pwndLeft->RemoveViewData(nViewLine);
4617 if (bCheckRight)
4619 m_pwndRight->RemoveViewData(nViewLine);
4621 if (bCheckBottom)
4623 m_pwndBottom->RemoveViewData(nViewLine);
4625 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4627 SaveUndoStep();
4629 nViewLineCount--;
4630 nRemovedCount++;
4631 continue;
4633 nViewLine++;
4635 return nRemovedCount;
4638 int CBaseView::FindScreenLineForViewLine( int viewLine )
4640 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4643 int CBaseView::CountMultiLines( int nViewLine )
4645 if (m_ScreenedViewLine.empty())
4646 return 0; // in case the view is completely empty
4648 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4650 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4652 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4655 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4657 TScreenedViewLine oScreenedLine;
4658 // tokenize string
4659 int prevpos = 0;
4660 int pos = 0;
4661 while ((pos = multiline.Find('\n', pos)) >= 0)
4663 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4664 pos++;
4665 prevpos = pos;
4667 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4668 oScreenedLine.bSublinesSet = true;
4669 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4671 return CountMultiLines(nViewLine);
4674 /// prepare inline diff cache
4675 LineColors & CBaseView::GetLineColors(int nViewLine)
4677 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4679 if (m_bWhitespaceInlineDiffs)
4681 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4682 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4684 else
4686 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4687 return m_ScreenedViewLine[nViewLine].lineColors;
4690 LineColors oLineColors;
4691 // set main line color
4692 COLORREF crBkgnd, crText;
4693 DiffStates diffState = m_pViewData->GetState(nViewLine);
4694 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4695 oLineColors.SetColor(0, crText, crBkgnd);
4697 do {
4698 if (!m_bShowInlineDiff)
4699 break;
4701 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4702 break;
4704 CString sLine = GetViewLineChars(nViewLine);
4705 if (sLine.IsEmpty())
4706 break;
4707 if (!m_pOtherView)
4708 break;
4710 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4711 if (sDiffLine.IsEmpty())
4712 break;
4714 svn_diff_t * diff = NULL;
4715 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4716 break;
4717 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4718 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4719 break;
4721 int lineoffset = 0;
4722 int nTextStartOffset = 0;
4723 std::map<int, COLORREF> removedPositions;
4724 while (diff)
4726 apr_off_t len = diff->original_length;
4728 CString s;
4729 for (int i = 0; i < len; ++i)
4731 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4732 lineoffset++;
4734 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4735 int nTextLength = s.GetLength();
4737 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4738 if ((m_bShowInlineDiff)&&(bInlineDiff))
4740 crBkgnd = InlineViewLineDiffColor(nViewLine);
4742 else
4744 crBkgnd = m_ModifiedBk;
4747 if (len < diff->modified_length)
4749 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4751 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4753 nTextStartOffset += nTextLength;
4754 diff = diff->next;
4756 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4758 oLineColors.AddShotColor(it->first, it->second);
4760 } while (false); // error catch
4762 if (!m_bWhitespaceInlineDiffs)
4764 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4765 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4767 else
4769 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4770 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4773 return GetLineColors(nViewLine);
4776 void CBaseView::OnEditSelectall()
4778 if (m_pViewData == nullptr)
4779 return;
4780 int nLastViewLine = m_pViewData->GetCount()-1;
4781 if (nLastViewLine < 0)
4782 return;
4783 SetupAllViewSelection(0, nLastViewLine);
4785 CString sLine = GetViewLineChars(nLastViewLine);
4786 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4787 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4788 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4790 UpdateWindow();
4793 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4795 FilterWhitespaces(first);
4796 FilterWhitespaces(second);
4799 void CBaseView::FilterWhitespaces(CString& line)
4801 line.Remove(' ');
4802 line.Remove('\t');
4803 line.Remove('\r');
4804 line.Remove('\n');
4807 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4809 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4810 int nEventLine = nLineFromTop + m_nTopLine;
4811 nEventLine--; //we need the index
4812 return nEventLine;
4816 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4818 if (RelayTrippleClick(pMsg))
4819 return TRUE;
4820 return CView::PreTranslateMessage(pMsg);
4824 void CBaseView::ResetUndoStep()
4826 m_AllState.Clear();
4829 void CBaseView::SaveUndoStep()
4831 if (!m_AllState.IsEmpty())
4833 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4835 ResetUndoStep();
4838 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4840 m_pState->addedlines.push_back(index);
4841 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4844 void CBaseView::InsertViewData( int index, const viewdata& data )
4846 m_pState->addedlines.push_back(index);
4847 m_pViewData->InsertData(index, data);
4850 void CBaseView::RemoveViewData( int index )
4852 m_pState->removedlines[index] = m_pViewData->GetData(index);
4853 m_pViewData->RemoveData(index);
4856 void CBaseView::SetViewData( int index, const viewdata& data )
4858 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4859 m_pViewData->SetData(index, data);
4862 void CBaseView::SetViewState( int index, DiffStates state )
4864 m_pState->linestates[index] = m_pViewData->GetState(index);
4865 m_pViewData->SetState(index, state);
4868 void CBaseView::SetViewLine( int index, const CString& sLine )
4870 m_pState->difflines[index] = m_pViewData->GetLine(index);
4871 m_pViewData->SetLine(index, sLine);
4874 void CBaseView::SetViewLineNumber( int index, int linenumber )
4876 int oldLineNumber = m_pViewData->GetLineNumber(index);
4877 if (oldLineNumber != linenumber) {
4878 m_pState->linelines[index] = oldLineNumber;
4879 m_pViewData->SetLineNumber(index, linenumber);
4883 void CBaseView::SetViewLineEnding( int index, EOL ending )
4885 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4886 m_pViewData->SetLineEnding(index, ending);
4889 void CBaseView::SetViewMarked( int index, bool marked )
4891 m_pViewData->SetMarked(index, marked);
4895 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4897 if (HasSelection())
4899 start = m_nSelViewBlockStart;
4900 end = m_nSelViewBlockEnd;
4901 return true;
4903 return false;
4906 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4908 RebuildIfNecessary();
4909 if (size() <= screenLine)
4910 return 0;
4911 return m_Screen2View[screenLine].nViewLine;
4914 int CBaseView::Screen2View::size()
4916 RebuildIfNecessary();
4917 return (int)m_Screen2View.size();
4920 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4922 RebuildIfNecessary();
4923 if (size() <= screenLine)
4924 return 0;
4925 return m_Screen2View[screenLine].nViewSubLine;
4928 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4930 RebuildIfNecessary();
4931 return m_Screen2View[screenLine];
4935 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4937 void CBaseView::Screen2View::RebuildIfNecessary()
4939 if (!m_pViewData)
4940 return; // rebuild not necessary
4942 FixScreenedCacheSize(m_pwndLeft);
4943 FixScreenedCacheSize(m_pwndRight);
4944 FixScreenedCacheSize(m_pwndBottom);
4945 if (!m_bFull)
4947 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4949 ResetScreenedViewLineCache(m_pwndLeft, *it);
4950 ResetScreenedViewLineCache(m_pwndRight, *it);
4951 ResetScreenedViewLineCache(m_pwndBottom, *it);
4954 else
4956 ResetScreenedViewLineCache(m_pwndLeft);
4957 ResetScreenedViewLineCache(m_pwndRight);
4958 ResetScreenedViewLineCache(m_pwndBottom);
4960 m_RebuildRanges.clear();
4961 m_bFull = false;
4963 size_t OldSize = m_Screen2View.size();
4964 m_Screen2View.clear();
4965 m_Screen2View.reserve(OldSize); // guess same size
4966 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4968 if (m_pMainFrame->m_bCollapsed)
4970 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4971 ++i;
4972 if (!(i < m_pViewData->GetCount()))
4973 break;
4975 TScreenLineInfo oLineInfo;
4976 oLineInfo.nViewLine = i;
4977 oLineInfo.nViewSubLine = -1; // no wrap
4978 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4980 int nMaxLines = 0;
4981 if (IsLeftViewGood())
4982 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4983 if (IsRightViewGood())
4984 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4985 if (IsBottomViewGood())
4986 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4987 for (int l = 0; l < (nMaxLines-1); ++l)
4989 oLineInfo.nViewSubLine++;
4990 m_Screen2View.push_back(oLineInfo);
4992 oLineInfo.nViewSubLine++;
4994 m_Screen2View.push_back(oLineInfo);
4996 m_pViewData = NULL;
4998 if (IsLeftViewGood())
4999 m_pwndLeft->BuildMarkedWordArray();
5000 if (IsRightViewGood())
5001 m_pwndRight->BuildMarkedWordArray();
5002 if (IsBottomViewGood())
5003 m_pwndBottom->BuildMarkedWordArray();
5004 UpdateLocator();
5005 RecalcAllVertScrollBars();
5006 RecalcAllHorzScrollBars();
5009 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
5011 RebuildIfNecessary();
5013 int nScreenLineCount = (int)m_Screen2View.size();
5015 int nPos = 0;
5016 if (nScreenLineCount>16)
5018 // for enough long data search for last screen
5019 // with viewline less than one we are looking for
5020 // use approximate method (based on) binary search using asymmetric start point
5021 // in form 2**n (determined as MSB of length) to go around division and rounding;
5022 // this effectively looks for bit values from MSB to LSB
5024 int nTestBit;
5025 //GetMostSignificantBitValue
5026 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5027 nTestBit = nScreenLineCount;
5028 nTestBit |= nTestBit>>1;
5029 nTestBit |= nTestBit>>2;
5030 nTestBit |= nTestBit>>4;
5031 nTestBit |= nTestBit>>8;
5032 nTestBit |= nTestBit>>16;
5033 nTestBit ^= (nTestBit>>1);
5035 while (nTestBit)
5037 int nTestPos = nPos | nTestBit;
5038 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
5040 nPos = nTestPos;
5042 nTestBit >>= 1;
5045 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
5047 nPos++;
5050 return nPos;
5053 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
5054 m_bFull = true;
5056 m_pViewData = pViewData;
5059 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
5061 if (m_bFull)
5062 return;
5064 m_pViewData = pViewData;
5066 TRebuildRange Range;
5067 Range.FirstViewLine=nFirstViewLine;
5068 Range.LastViewLine=nLastViewLine;
5069 m_RebuildRanges.push_back(Range);
5072 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
5074 if (!IsViewGood(pwndView))
5076 return false;
5078 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
5079 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
5080 if (nOldSize == nViewCount)
5082 return false;
5084 pwndView->m_ScreenedViewLine.resize(nViewCount);
5085 return true;
5088 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView) const
5090 if (!IsViewGood(pwndView))
5092 return false;
5094 TRebuildRange Range={0, pwndView->GetViewCount()-1};
5095 ResetScreenedViewLineCache(pwndView, Range);
5096 return true;
5099 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range) const
5101 if (!IsViewGood(pwndView))
5103 return false;
5105 if (Range.LastViewLine == -1)
5107 return false;
5109 ASSERT(Range.FirstViewLine >= 0);
5110 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
5111 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
5113 pwndView->m_ScreenedViewLine[i].Clear();
5115 return false;
5118 void CBaseView::WrapChanged()
5120 m_nMaxLineLength = -1;
5121 m_nOffsetChar = 0;
5124 void CBaseView::OnEditFind()
5126 if (m_pFindDialog)
5127 return;
5129 m_pFindDialog = new CFindDlg(this);
5130 m_pFindDialog->Create(this);
5132 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
5133 m_pFindDialog->SetReadonly(m_bReadonly);
5136 LRESULT CBaseView::OnFindDialogMessage(WPARAM wParam, LPARAM /*lParam*/)
5138 ASSERT(m_pFindDialog != NULL);
5140 if (m_pFindDialog->IsTerminating())
5142 // invalidate the handle identifying the dialog box.
5143 m_pFindDialog = NULL;
5144 return 0;
5147 if(m_pFindDialog->FindNext())
5149 //read data from dialog
5150 m_sFindText = m_pFindDialog->GetFindString();
5151 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
5152 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
5153 m_bWholeWord = m_pFindDialog->WholeWord();
5155 if (!m_bMatchCase)
5156 m_sFindText = m_sFindText.MakeLower();
5158 BuildFindStringArray();
5159 if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Find)
5161 if (m_pFindDialog->SearchUp())
5162 OnEditFindprev();
5163 else
5164 OnEditFindnext();
5166 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Count)
5168 size_t count = 0;
5169 for (size_t i = 0; i < m_arFindStringLines.size(); ++i)
5170 count += m_arFindStringLines[i];
5171 CString format;
5172 format.LoadString(IDS_FIND_COUNT);
5173 CString matches;
5174 matches.Format(format, count);
5175 m_pFindDialog->SetStatusText(matches);
5177 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::Replace)
5179 if (!IsWritable())
5180 return 0;
5181 bool bFound = false;
5182 if (m_pFindDialog->SearchUp())
5183 bFound = Search(SearchPrevious, true, true, false);
5184 else
5185 bFound = Search(SearchNext, true, true, false);
5186 if (bFound)
5188 CString sReplaceText = m_pFindDialog->GetReplaceString();
5189 CUndo::GetInstance().BeginGrouping();
5190 RemoveSelectedText();
5191 InsertText(sReplaceText);
5192 CUndo::GetInstance().EndGrouping();
5196 else if ((CFindDlg::FindType)wParam == CFindDlg::FindType::ReplaceAll)
5198 if (!IsWritable())
5199 return 0;
5200 bool bFound = false;
5201 int replaceCount = 0;
5202 POINT lastPoint = m_ptSelectionViewPosStart;
5203 m_ptSelectionViewPosStart.x = m_ptSelectionViewPosStart.y = 0;
5204 CUndo::GetInstance().BeginGrouping();
5207 bFound = Search(SearchNext, true, false, true);
5208 if (bFound)
5210 CString sReplaceText = m_pFindDialog->GetReplaceString();
5211 RemoveSelectedText();
5212 InsertText(sReplaceText);
5213 ++replaceCount;
5215 } while (bFound);
5216 CUndo::GetInstance().EndGrouping();
5217 if (replaceCount == 0)
5218 m_ptSelectionViewPosStart = lastPoint;
5219 CString message;
5220 message.Format(IDS_FIND_REPLACED, replaceCount);
5221 if (m_pFindDialog)
5222 m_pFindDialog->SetStatusText(message, RGB(0, 0, 0));
5227 return 0;
5230 void CBaseView::OnEditFindnextStart()
5232 if (m_pViewData == nullptr)
5233 return;
5234 if (HasTextSelection())
5236 m_sFindText = GetSelectedText();
5237 m_bMatchCase = false;
5238 m_bLimitToDiff = false;
5239 m_bWholeWord = false;
5240 m_sFindText = m_sFindText.MakeLower();
5242 BuildFindStringArray();
5243 OnEditFindnext();
5245 else
5247 m_sFindText.Empty();
5248 BuildFindStringArray();
5252 void CBaseView::OnEditFindprevStart()
5254 if (m_pViewData == nullptr)
5255 return;
5256 if (HasTextSelection())
5258 m_sFindText = GetSelectedText();
5259 m_bMatchCase = false;
5260 m_bLimitToDiff = false;
5261 m_bWholeWord = false;
5262 m_sFindText = m_sFindText.MakeLower();
5264 BuildFindStringArray();
5265 OnEditFindprev();
5267 else
5269 m_sFindText.Empty();
5270 BuildFindStringArray();
5274 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5276 if (srchDir == SearchPrevious)
5278 int laststart = -1;
5279 int laststart2 = -1;
5282 laststart2 = laststart;
5283 laststart = str.Find(m_sFindText, laststart + 1);
5284 } while (laststart >= 0 && laststart < start);
5285 start = laststart2;
5287 else
5288 start = str.Find(m_sFindText, start);
5289 end = start + m_sFindText.GetLength();
5290 bool bStringFound = (start >= 0);
5291 if (bStringFound && m_bWholeWord)
5293 if (start)
5294 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5296 if (bStringFound)
5298 if (str.GetLength() > end)
5299 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5302 return bStringFound;
5305 void CBaseView::OnEditFindprev()
5307 Search(SearchPrevious, false, true, false);
5310 void CBaseView::OnEditFindnext()
5312 Search(SearchNext, false, true, false);
5315 bool CBaseView::Search(SearchDirection srchDir, bool useStart, bool flashIfNotFound, bool stopEof)
5317 if (m_sFindText.IsEmpty())
5318 return false;
5319 if(!m_pViewData)
5320 return false;
5322 POINT start = useStart ? m_ptSelectionViewPosStart : m_ptSelectionViewPosEnd;
5323 POINT end;
5324 end.y = m_pViewData->GetCount()-1;
5325 if (end.y < 0)
5326 return false;
5328 if (srchDir==SearchNext)
5329 end.x = GetViewLineLength(end.y);
5330 else
5332 end.x = m_ptSelectionViewPosStart.x;
5333 start.x = 0;
5336 if (!HasTextSelection())
5338 start.y = m_ptCaretViewPos.y;
5339 if (srchDir==SearchNext)
5340 start.x = m_ptCaretViewPos.x;
5341 else
5343 start.x = 0;
5344 end.x = m_ptCaretViewPos.x;
5347 CString sSelectedText;
5348 int startline = -1;
5349 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5351 if (nViewLine < 0)
5353 if (stopEof)
5354 return false;
5355 nViewLine = m_pViewData->GetCount()-1;
5356 startline = start.y;
5357 if (flashIfNotFound)
5359 if (m_pFindDialog)
5360 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED)), RGB(63, 127, 47));
5361 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5364 if (nViewLine > end.y)
5366 if (stopEof)
5367 return false;
5368 nViewLine = 0;
5369 startline = start.y;
5370 if (flashIfNotFound)
5372 if (m_pFindDialog)
5373 m_pFindDialog->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED)), RGB(63, 127, 47));
5374 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 2, 100);
5377 switch (m_pViewData->GetState(nViewLine))
5379 case DIFFSTATE_EMPTY:
5380 break;
5381 case DIFFSTATE_UNKNOWN:
5382 case DIFFSTATE_NORMAL:
5383 if (m_bLimitToDiff)
5384 break;
5385 case DIFFSTATE_REMOVED:
5386 case DIFFSTATE_REMOVEDWHITESPACE:
5387 case DIFFSTATE_ADDED:
5388 case DIFFSTATE_ADDEDWHITESPACE:
5389 case DIFFSTATE_WHITESPACE:
5390 case DIFFSTATE_WHITESPACE_DIFF:
5391 case DIFFSTATE_CONFLICTED:
5392 case DIFFSTATE_CONFLICTED_IGNORED:
5393 case DIFFSTATE_CONFLICTADDED:
5394 case DIFFSTATE_CONFLICTEMPTY:
5395 case DIFFSTATE_CONFLICTRESOLVED:
5396 case DIFFSTATE_IDENTICALREMOVED:
5397 case DIFFSTATE_IDENTICALADDED:
5398 case DIFFSTATE_THEIRSREMOVED:
5399 case DIFFSTATE_THEIRSADDED:
5400 case DIFFSTATE_YOURSREMOVED:
5401 case DIFFSTATE_YOURSADDED:
5402 case DIFFSTATE_EDITED:
5404 sSelectedText = GetViewLineChars(nViewLine);
5405 if (nViewLine == start.y && startline < 0)
5406 sSelectedText = srchDir == SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(end.x);
5407 if (!m_bMatchCase)
5408 sSelectedText = sSelectedText.MakeLower();
5409 int startfound = srchDir == SearchNext ? 0 : sSelectedText.GetLength();
5410 int endfound = 0;
5411 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5413 HighlightViewLines(nViewLine, nViewLine);
5414 m_ptSelectionViewPosStart.x = startfound;
5415 m_ptSelectionViewPosEnd.x = endfound;
5416 if (nViewLine == start.y && startline < 0)
5418 m_ptSelectionViewPosStart.x += start.x;
5419 m_ptSelectionViewPosEnd.x += start.x;
5421 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5422 m_ptSelectionViewPosStart.y = nViewLine;
5423 m_ptSelectionViewPosEnd.y = nViewLine;
5424 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5425 UpdateViewsCaretPosition();
5426 EnsureCaretVisible();
5427 Invalidate();
5428 return true;
5431 break;
5434 if (startline >= 0)
5436 if (nViewLine == startline)
5438 if (flashIfNotFound)
5440 CString message;
5441 message.Format(IDS_FIND_NOTFOUND, m_sFindText);
5442 if (m_pFindDialog)
5443 m_pFindDialog->SetStatusText(message, RGB(255, 0, 0));
5444 ::MessageBeep(0xFFFFFFFF);
5445 m_pMainFrame->FlashWindowEx(FLASHW_ALL, 3, 100);
5447 break;
5451 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5452 return false;
5455 CString CBaseView::GetSelectedText() const
5457 CString sSelectedText;
5458 POINT start = m_ptSelectionViewPosStart;
5459 POINT end = m_ptSelectionViewPosEnd;
5460 if (!HasTextSelection())
5462 if (!HasSelection())
5463 return sSelectedText;
5464 start.y = m_nSelViewBlockStart;
5465 start.x = 0;
5466 end.y = m_nSelViewBlockEnd;
5467 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5469 if (m_pViewData == nullptr)
5470 return sSelectedText;
5471 // first store the selected lines in one CString
5472 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5474 switch (m_pViewData->GetState(nViewLine))
5476 case DIFFSTATE_EMPTY:
5477 break;
5478 case DIFFSTATE_UNKNOWN:
5479 case DIFFSTATE_NORMAL:
5480 case DIFFSTATE_REMOVED:
5481 case DIFFSTATE_REMOVEDWHITESPACE:
5482 case DIFFSTATE_ADDED:
5483 case DIFFSTATE_ADDEDWHITESPACE:
5484 case DIFFSTATE_WHITESPACE:
5485 case DIFFSTATE_WHITESPACE_DIFF:
5486 case DIFFSTATE_CONFLICTED:
5487 case DIFFSTATE_CONFLICTED_IGNORED:
5488 case DIFFSTATE_CONFLICTADDED:
5489 case DIFFSTATE_CONFLICTEMPTY:
5490 case DIFFSTATE_CONFLICTRESOLVED:
5491 case DIFFSTATE_IDENTICALREMOVED:
5492 case DIFFSTATE_IDENTICALADDED:
5493 case DIFFSTATE_THEIRSREMOVED:
5494 case DIFFSTATE_THEIRSADDED:
5495 case DIFFSTATE_YOURSREMOVED:
5496 case DIFFSTATE_YOURSADDED:
5497 case DIFFSTATE_EDITED:
5498 sSelectedText += GetViewLineChars(nViewLine);
5499 sSelectedText += _T("\r\n");
5500 break;
5503 // remove the non-selected chars from the first line, last line and last \r\n
5504 int nLeftCut = start.x;
5505 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5506 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5507 return sSelectedText;
5510 void CBaseView::CheckModifications(bool& hasMods, bool& hasConflicts, bool& hasWhitespaceMods)
5512 hasMods = false;
5513 hasConflicts = false;
5514 hasWhitespaceMods = false;
5516 if (m_pViewData)
5518 for (int i=0; i<m_pViewData->GetCount(); i++)
5520 DiffStates state = m_pViewData->GetState(i);
5521 switch (state)
5523 case DIFFSTATE_ADDED:
5524 case DIFFSTATE_IDENTICALADDED:
5525 case DIFFSTATE_THEIRSADDED:
5526 case DIFFSTATE_YOURSADDED:
5527 case DIFFSTATE_CONFLICTADDED:
5528 case DIFFSTATE_IDENTICALREMOVED:
5529 case DIFFSTATE_REMOVED:
5530 case DIFFSTATE_THEIRSREMOVED:
5531 case DIFFSTATE_YOURSREMOVED:
5532 case DIFFSTATE_EMPTY:
5533 hasMods = true;
5534 break;
5535 case DIFFSTATE_CONFLICTED:
5536 case DIFFSTATE_CONFLICTED_IGNORED:
5537 hasConflicts = true;
5538 break;
5539 case DIFFSTATE_REMOVEDWHITESPACE:
5540 case DIFFSTATE_ADDEDWHITESPACE:
5541 case DIFFSTATE_WHITESPACE:
5542 case DIFFSTATE_WHITESPACE_DIFF:
5543 hasWhitespaceMods = true;
5544 break;
5550 void CBaseView::OnEditGotoline()
5552 if (m_pViewData == NULL)
5553 return;
5554 // find the last and first line number
5555 int nViewLineCount = m_pViewData->GetCount();
5557 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5558 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5560 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5561 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5563 break;
5566 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5568 return;
5570 nLastLineNumber++;
5571 int nFirstLineNumber=1; // first is always 1
5573 CString sText;
5574 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5576 CGotoLineDlg dlg(this);
5577 dlg.SetLabel(sText);
5578 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5579 if (dlg.DoModal() == IDOK)
5581 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5583 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5585 HighlightViewLines(nViewLine, nViewLine);
5586 return;
5592 void CBaseView::OnToggleReadonly()
5594 if (IsReadonlyChangable()) {
5595 SetWritable(IsReadonly());
5599 int CBaseView::SaveFile(int nFlags)
5601 Invalidate();
5602 if (m_pViewData!=NULL && m_pWorkingFile!=NULL)
5604 CFileTextLines file;
5605 m_SaveParams.m_LineEndings = m_lineendings;
5606 m_SaveParams.m_UnicodeType = m_texttype;
5607 file.SetSaveParams(m_SaveParams);
5609 for (int i=0; i<m_pViewData->GetCount(); i++)
5611 //only copy non-removed lines
5612 DiffStates state = m_pViewData->GetState(i);
5613 switch (state)
5615 case DIFFSTATE_CONFLICTED:
5616 case DIFFSTATE_CONFLICTED_IGNORED:
5618 int first = i;
5619 int last = i;
5622 last++;
5623 } while((last<m_pViewData->GetCount()) && ((m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(m_pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
5624 file.Add(_T("<<<<<<< .mine"), EOL_NOENDING);
5625 for (int j=first; j<last; j++)
5627 file.Add(m_pwndRight->m_pViewData->GetLine(j), m_pwndRight->m_pViewData->GetLineEnding(j));
5629 file.Add(_T("======="), EOL_NOENDING);
5630 for (int j=first; j<last; j++)
5632 file.Add(m_pwndLeft->m_pViewData->GetLine(j), m_pwndLeft->m_pViewData->GetLineEnding(j));
5634 file.Add(_T(">>>>>>> .theirs"), EOL_NOENDING);
5635 i = last-1;
5637 break;
5638 case DIFFSTATE_EMPTY:
5639 break;
5640 case DIFFSTATE_CONFLICTEMPTY:
5641 case DIFFSTATE_IDENTICALREMOVED:
5642 case DIFFSTATE_REMOVED:
5643 case DIFFSTATE_THEIRSREMOVED:
5644 case DIFFSTATE_YOURSREMOVED:
5645 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
5646 if ((nFlags&SAVE_REMOVEDLINES) == 0)
5648 // do not save removed lines
5649 break;
5651 default:
5652 file.Add(m_pViewData->GetLine(i), m_pViewData->GetLineEnding(i));
5653 break;
5656 CString filename = m_pWorkingFile->GetFilename();
5657 if (m_pWorkingFile->IsReadonly())
5658 if (!CCommonAppUtils::FileOpenSave(filename, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd))
5659 return -1;
5660 if (!file.Save(filename))
5662 ::MessageBox(m_hWnd, file.GetErrorString(), _T("TortoiseGitMerge"), MB_ICONERROR);
5663 return -1;
5665 m_pWorkingFile->SetFileName(filename);
5666 m_pWorkingFile->StoreFileAttributes();
5667 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5668 SetModified(FALSE);
5669 CUndo::GetInstance().MarkAsOriginalState(
5670 this == m_pwndLeft,
5671 this == m_pwndRight,
5672 this == m_pwndBottom);
5673 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
5674 return 0;
5675 return file.GetCount();
5677 return 1;
5681 int CBaseView::SaveFileTo(CString sFileName, int nFlags)
5683 if (m_pWorkingFile)
5685 m_pWorkingFile->SetFileName(sFileName);
5686 return SaveFile(nFlags);
5688 return -1;
5692 EOL CBaseView::GetLineEndings()
5694 return GetLineEndings(GetWhitecharsProperties().HasMixedEols);
5697 EOL CBaseView::GetLineEndings(bool bHasMixedEols)
5699 if (bHasMixedEols)
5701 return EOL_AUTOLINE; // mixed eols - hack value
5703 if (m_lineendings == EOL_AUTOLINE)
5705 return EOL_CRLF;
5707 return m_lineendings;
5710 void CBaseView::ReplaceLineEndings(EOL eEol)
5712 if (eEol == EOL_AUTOLINE)
5714 return;
5716 // set AUTOLINE
5717 m_lineendings = eEol;
5718 // replace all set EOLs
5719 // TODO store line endings and lineendings in undo
5720 //CUndo::BeginGrouping();
5721 for (int i = 0; i < GetViewCount(); ++i)
5723 if (IsLineEmpty(i))
5725 continue;
5727 EOL eLineEol = GetViewLineEnding(i);
5728 if (eLineEol == EOL_AUTOLINE || eLineEol == EOL_NOENDING || eLineEol == m_lineendings)
5730 continue;
5732 SetViewLineEnding(i, eEol);
5734 //CUndo::EndGrouping();
5735 //CUndo::saveundostep;
5736 DocumentUpdated();
5737 SetModified();
5740 void CBaseView::SetLineEndingStyle(EOL eEol)
5742 m_lineendings = eEol;
5745 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType)
5747 if (m_texttype == eTextType)
5749 return;
5751 m_texttype = eTextType;
5752 DocumentUpdated();
5753 SetModified();
5756 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId)
5758 if (IsReadonly())
5759 return; // nothing to be changed in read-only view
5760 CEncodingDlg dlg;
5761 dlg.view = CString(MAKEINTRESOURCE(nTextId));
5762 dlg.texttype = m_texttype;
5763 dlg.lineendings = GetLineEndings();
5764 if (dlg.DoModal() != IDOK)
5765 return;
5766 SetTextType(dlg.texttype);
5767 ReplaceLineEndings(dlg.lineendings);
5771 Replaces lines from source view to this
5773 void CBaseView::UseViewBlock(CBaseView * pwndView, int nFirstViewLine, int nLastViewLine, bool skipMarked)
5775 if (!IsViewGood(pwndView))
5776 return;
5777 if (!IsWritable())
5778 return;
5779 CUndo::GetInstance().BeginGrouping();
5781 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5783 if (skipMarked && GetViewMarked(viewLine))
5784 continue;
5785 viewdata line = pwndView->GetViewData(viewLine);
5786 if (line.ending != EOL_NOENDING)
5787 line.ending = m_lineendings;
5788 switch (line.state)
5790 case DIFFSTATE_CONFLICTEMPTY:
5791 case DIFFSTATE_UNKNOWN:
5792 line.state = DIFFSTATE_EMPTY;
5793 case DIFFSTATE_EMPTY:
5794 break;
5795 case DIFFSTATE_ADDED:
5796 case DIFFSTATE_CONFLICTADDED:
5797 case DIFFSTATE_CONFLICTED:
5798 case DIFFSTATE_CONFLICTED_IGNORED:
5799 case DIFFSTATE_IDENTICALADDED:
5800 case DIFFSTATE_THEIRSADDED:
5801 case DIFFSTATE_YOURSADDED:
5802 case DIFFSTATE_IDENTICALREMOVED:
5803 case DIFFSTATE_REMOVED:
5804 case DIFFSTATE_THEIRSREMOVED:
5805 case DIFFSTATE_YOURSREMOVED:
5806 pwndView->SetViewState(viewLine, DIFFSTATE_NORMAL);
5807 line.state = DIFFSTATE_NORMAL;
5808 case DIFFSTATE_NORMAL:
5809 break;
5810 default:
5811 break;
5813 SetViewData(viewLine, line);
5814 if ((m_texttype == UnicodeType::ASCII) && (pwndView->GetTextType() != UnicodeType::ASCII))
5816 // if this view is in ASCII and the other is not, we have to make sure that
5817 // the text we copy from the other view can actually be saved in ASCII encoding.
5818 // if not, we have to change this views encoding to the same encoding as the other view
5819 BOOL useDefault = FALSE;
5820 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, line.sLine, -1, NULL, 0, 0, &useDefault);
5821 if (useDefault) // a default char is required, so the char can not be saved as ASCII
5822 SetTextType(pwndView->GetTextType());
5825 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
5826 // TODO: check if copied line is same as original one set modified only when differ
5827 SetModified();
5828 SaveUndoStep();
5830 int nRemovedLines = CleanEmptyLines();
5831 SaveUndoStep();
5832 //VerifyEols();
5833 // make sure all non empty line have EOL set but last
5834 // wrong can be last copied line(have eol, but no line under),
5835 // or old last line (line before copied block missing eol, but have line under)
5836 // we'll check all lines to be sure
5837 int nLine = GetViewCount();
5838 // check last line have no EOL set
5839 while (--nLine>=0)
5841 if (!IsViewLineEmpty(nLine))
5843 if (GetViewLineEnding(nLine) != EOL_NOENDING)
5845 // we added non last line into empty block on the end (or should we remove eol from this one ?)
5846 // so next line is empty
5847 ASSERT(IsViewLineEmpty(nLine+1));
5848 // and we can turn it to normal empty line
5849 SetViewData(nLine+1, viewdata(CString(), DIFFSTATE_ADDED, 1, EOL_NOENDING, HIDESTATE_SHOWN));
5851 break;
5854 // check all (nonlast) line have EOL set
5855 while (--nLine>=0)
5857 if (!IsViewLineEmpty(nLine))
5859 if (GetViewLineEnding(nLine) == EOL_NOENDING)
5861 SetViewLineEnding(nLine, m_lineendings);
5862 // in theory there should be only one line needing fix, but most of time we get over all anyway
5863 // break;
5867 SaveUndoStep();
5868 UpdateViewLineNumbers();
5869 SaveUndoStep();
5871 CUndo::GetInstance().EndGrouping();
5873 if (nRemovedLines!=0)
5875 // some lines are gone update selection
5876 ClearSelection();
5877 SetupAllViewSelection(nFirstViewLine, nLastViewLine - nRemovedLines);
5879 BuildAllScreen2ViewVector();
5880 pwndView->Invalidate();
5881 RefreshViews();
5884 void CBaseView::MarkBlock(bool marked, int nFirstViewLine, int nLastViewLine)
5886 for (int viewLine = nFirstViewLine; viewLine <= nLastViewLine; viewLine++)
5887 SetViewMarked(viewLine, marked);
5888 BuildAllScreen2ViewVector();
5889 Invalidate();
5890 RefreshViews();
5893 void CBaseView::UseViewFileExceptMarked(CBaseView *pwndView)
5895 UseViewBlock(pwndView, 0, GetViewCount() - 1, true);
5898 int CBaseView::GetIndentCharsForLine(int x, int y)
5900 const int maxGuessLine = 100;
5901 int nTabMode = -1;
5902 CString line = GetViewLine(y);
5903 if (m_nTabMode & TABMODE_SMARTINDENT)
5905 // detect left char and right char
5906 TCHAR lc = x > 0 ? line[x - 1] : '\0';
5907 TCHAR rc = x < line.GetLength() ? line[x] : '\0';
5908 if (lc == ' ' && rc != '\t' || rc == ' ' && lc != '\t')
5909 nTabMode = 1;
5910 if (lc == '\t' && rc != ' ' || rc == '\t' && lc != ' ')
5911 nTabMode = 0;
5912 if (lc == ' ' && rc == '\t' || rc == ' ' && lc == '\t')
5913 nTabMode = m_nTabMode & TABMODE_USESPACES;
5915 // detect lines nearby
5916 for (int i = y - 1, j = y + 1; nTabMode == -1; --i, ++j)
5918 bool above = i > 0 && i >= y - maxGuessLine;
5919 bool below = j < GetViewCount() && j <= y + maxGuessLine;
5920 if (!(above || below))
5921 break;
5922 TCHAR ac = above ? GetViewLine(i)[0] : '\0';
5923 TCHAR bc = below ? GetViewLine(j)[0] : '\0';
5924 if (ac == ' ' && bc != '\t' || bc == ' ' && ac != '\t')
5925 nTabMode = 1;
5926 else if (ac == '\t' && bc != ' ' || bc == '\t' && ac != ' ')
5927 nTabMode = 0;
5928 else if (ac == ' ' && bc == '\t' || bc == ' ' && ac == '\t')
5929 nTabMode = m_nTabMode & TABMODE_USESPACES;
5932 else
5933 nTabMode = m_nTabMode & TABMODE_USESPACES;
5935 if (nTabMode > 0)
5937 // use spaces
5938 x = CountExpandedChars(line, x);
5939 return (m_nTabSize - (x % m_nTabSize));
5942 // use tab
5943 return 0;
5946 void CBaseView::AddIndentationForSelectedBlock()
5948 bool bModified = false;
5949 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
5951 // skip the line if no character is selected in the last selected line
5952 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
5954 continue;
5956 // skip empty lines
5957 if (IsLineEmpty(nViewLine))
5959 continue;
5961 const CString &sLine = GetViewLine(nViewLine);
5962 CString sTemp = sLine;
5963 if (sTemp.Trim().IsEmpty())
5965 // skip empty and whitechar only lines
5966 continue;
5968 // add tab to line start (alternatively m_nTabSize spaces can be used)
5969 CString tabStr;
5970 int indentChars = GetIndentCharsForLine(0, nViewLine);
5971 tabStr = indentChars > 0 ? CString(_T(' '), indentChars) : _T("\t");
5972 SetViewLine(nViewLine, tabStr + sLine);
5973 bModified = true;
5975 if (bModified)
5977 SetModified();
5978 SaveUndoStep();
5979 BuildAllScreen2ViewVector();
5983 void CBaseView::RemoveIndentationForSelectedBlock()
5985 bool bModified = false;
5986 for (int nViewLine = m_ptSelectionViewPosStart.y; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
5988 // skip the line if no character is selected in the last selected line
5989 if (nViewLine == m_ptSelectionViewPosEnd.y && m_ptSelectionViewPosEnd.x == 0)
5991 continue;
5993 // skip empty lines
5994 if (IsLineEmpty(nViewLine))
5996 continue;
5998 CString sLine = GetViewLine(nViewLine);
5999 // remove up to n spaces from line start
6000 // and one tab (if less then n spaces was removed)
6001 int nPos = 0;
6002 while (nPos<m_nTabSize)
6004 switch (sLine[nPos])
6006 case ' ':
6007 nPos++;
6008 continue;
6009 case '\t':
6010 nPos++;
6012 break;
6014 if (nPos>0)
6016 sLine.Delete(0, nPos);
6017 SetViewLine(nViewLine, sLine);
6018 bModified = true;
6021 if (bModified)
6023 SetModified();
6024 SaveUndoStep();
6025 BuildAllScreen2ViewVector();
6030 there are two possible versions
6031 - convert tabs to spaces only in front of text (implemented)
6032 - convert all tabs to spaces
6034 void CBaseView::ConvertTabToSpaces()
6036 bool bModified = false;
6037 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6039 if (IsLineEmpty(nViewLine))
6041 continue;
6043 const CString &sLine = GetViewLine(nViewLine);
6044 bool bTabToConvertFound = false;
6045 int nPosIn = 0;
6046 int nPosOut = 0;
6047 while (nPosIn<sLine.GetLength())
6049 switch (sLine[nPosIn])
6051 case ' ':
6052 nPosIn++;
6053 nPosOut++;
6054 continue;
6055 case '\t':
6056 nPosIn++;
6057 bTabToConvertFound = true;
6058 nPosOut = (nPosOut+m_nTabSize) - nPosOut%m_nTabSize;
6059 continue;
6061 break;
6063 if (bTabToConvertFound)
6065 CString sLineNew = sLine;
6066 sLineNew.Delete(0, nPosIn);
6067 sLineNew = CString(' ', nPosOut) + sLineNew;
6068 SetViewLine(nViewLine, sLineNew);
6069 bModified = true;
6072 if (bModified)
6074 SetModified();
6075 SaveUndoStep();
6076 BuildAllScreen2ViewVector();
6081 there are two possible version
6082 - convert spaces to tabs only in front of text (implemented)
6083 - convert all spaces to tabs
6085 void CBaseView::Tabularize()
6087 bool bModified = false;
6088 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6090 if (IsLineEmpty(nViewLine))
6092 continue;
6094 const CString &sLine = GetViewLine(nViewLine);
6095 int nDel = 0;
6096 int nTabCount = 0; // total tabs to be used
6097 int nSpaceCount = 0; // number of spaces in tab size run
6098 int nPos = 0;
6099 while (nPos<sLine.GetLength())
6101 switch (sLine[nPos++])
6103 case ' ':
6104 //bSpace = true;
6105 if (++nSpaceCount < m_nTabSize)
6107 continue;
6109 case '\t':
6110 nTabCount++;
6111 nSpaceCount = 0;
6112 nDel = nPos;
6113 continue;
6115 break;
6117 if (nDel > 0)
6119 CString sLineNew = sLine;
6120 sLineNew.Delete(0, nDel);
6121 sLineNew = CString('\t', nTabCount) + sLineNew;
6122 if (sLine!=sLineNew)
6124 SetViewLine(nViewLine, sLineNew);
6125 bModified = true;
6129 if (bModified)
6131 SetModified();
6132 SaveUndoStep();
6133 BuildAllScreen2ViewVector();
6137 void CBaseView::RemoveTrailWhiteChars()
6139 bool bModified = false;
6140 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6142 if (IsLineEmpty(nViewLine))
6144 continue;
6146 const CString &sLine = GetViewLine(nViewLine);
6147 CString sLineNew = sLine;
6148 sLineNew.TrimRight();
6149 if (sLine.GetLength()!=sLineNew.GetLength())
6151 SetViewLine(nViewLine, sLineNew);
6152 bModified = true;
6155 if (bModified)
6157 SetModified();
6158 SaveUndoStep();
6159 BuildAllScreen2ViewVector();
6163 CBaseView::TWhitecharsProperties CBaseView::GetWhitecharsProperties()
6165 if (GetViewCount()>10000)
6167 // 10k lines is enough to check
6168 TWhitecharsProperties oRet = {true, true, true, true};
6169 return oRet;
6171 TWhitecharsProperties oRet = {};
6172 for (int nViewLine = 0; nViewLine < GetViewCount(); nViewLine++)
6174 if (IsLineEmpty(nViewLine))
6176 continue;
6178 const CString &sLine = GetViewLine(nViewLine);
6179 if (sLine.IsEmpty())
6181 continue;
6183 // check leading whites for convertible tabs and spaces
6184 int nPos = 0;
6185 int nSpaceCount = 0; // number of spaces in tab size run
6186 while (nPos<sLine.GetLength() && (!oRet.HasSpacesToConvert || !oRet.HasTabsToConvert))
6188 switch (sLine[nPos++])
6190 case ' ':
6191 if (++nSpaceCount >= m_nTabSize)
6193 oRet.HasSpacesToConvert = true;
6195 continue;
6196 case '\t':
6197 oRet.HasTabsToConvert = true;
6198 if (nSpaceCount!=0)
6200 oRet.HasSpacesToConvert = true;
6202 continue;
6204 break;
6207 // check trailing whites for removable chars
6208 switch (sLine[sLine.GetLength()-1])
6210 case ' ':
6211 case '\t':
6212 oRet.HasTrailWhiteChars = true;
6215 // check EOLs
6216 EOL eLineEol = GetViewLineEnding(nViewLine);
6217 if (!oRet.HasMixedEols && (eLineEol != m_lineendings) && (eLineEol != EOL_AUTOLINE) && (eLineEol != EOL_NOENDING))
6219 oRet.HasMixedEols = true;
6222 return oRet;
6225 void CBaseView::InsertText(const CString& sText)
6227 ResetUndoStep();
6229 POINT ptCaretViewPos = GetCaretViewPosition();
6230 int nLeft = ptCaretViewPos.x;
6231 int nViewLine = ptCaretViewPos.y;
6233 if ((nViewLine == 0) && (GetViewCount() == 0))
6234 OnChar(VK_RETURN, 0, 0);
6236 std::vector<CString> lines;
6237 int nStart = 0;
6238 int nEolPos = 0;
6239 while ((nEolPos = sText.Find('\r', nEolPos)) >= 0)
6241 CString sLine = sText.Mid(nStart, nEolPos - nStart);
6242 lines.push_back(sLine);
6243 nEolPos++;
6244 nStart = nEolPos;
6246 CString sLine = sText.Mid(nStart);
6247 lines.push_back(sLine);
6249 int nLinesToPaste = (int)lines.size();
6250 if (nLinesToPaste > 1)
6252 // multiline text
6254 // We want to undo the multiline insertion in a single step.
6255 CUndo::GetInstance().BeginGrouping();
6257 sLine = GetViewLineChars(nViewLine);
6258 CString sLineLeft = sLine.Left(nLeft);
6259 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
6260 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
6261 viewdata newLine(L"", DIFFSTATE_EDITED, 1, m_lineendings, HIDESTATE_SHOWN);
6262 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding != m_lineendings))
6264 newLine.sLine = sLineLeft + lines[0];
6265 SetViewData(nViewLine, newLine);
6268 int nInsertLine = nViewLine;
6269 for (int i = 1; i < nLinesToPaste - 1; i++)
6271 newLine.sLine = lines[i];
6272 InsertViewData(++nInsertLine, newLine);
6274 newLine.sLine = lines[nLinesToPaste - 1] + sLineRight;
6275 newLine.ending = eOriginalEnding;
6276 InsertViewData(++nInsertLine, newLine);
6278 SetModified();
6279 SaveUndoStep();
6281 // adds new lines everywhere except me
6282 if (IsViewGood(m_pwndLeft) && m_pwndLeft != this)
6284 m_pwndLeft->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6286 if (IsViewGood(m_pwndRight) && m_pwndRight != this)
6288 m_pwndRight->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6290 if (IsViewGood(m_pwndBottom) && m_pwndBottom != this)
6292 m_pwndBottom->InsertViewEmptyLines(nViewLine + 1, nLinesToPaste - 1);
6294 SaveUndoStep();
6296 UpdateViewLineNumbers();
6297 CUndo::GetInstance().EndGrouping();
6299 ptCaretViewPos = SetupPoint(lines[nLinesToPaste - 1].GetLength(), nInsertLine);
6301 else
6303 // single line text - just insert it
6304 sLine = GetViewLineChars(nViewLine);
6305 sLine.Insert(nLeft, sText);
6306 ptCaretViewPos = SetupPoint(nLeft + sText.GetLength(), nViewLine);
6307 SetViewLine(nViewLine, sLine);
6308 SetViewState(nViewLine, DIFFSTATE_EDITED);
6309 SetModified();
6310 SaveUndoStep();
6313 RefreshViews();
6314 BuildAllScreen2ViewVector();
6315 UpdateCaretViewPosition(ptCaretViewPos);