1
// TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2019 - TortoiseSVN
4 // Copyright (C) 2011-2012, 2017-2019 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.
23 #include "TortoiseMerge.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
29 #include "GotoLineDlg.h"
30 #include "EncodingDlg.h"
31 #include "EditorConfigWrapper.h"
35 // We use three different kind of lines here:
36 // 1. The real lines of the original files.
37 // These are shown in the view margin and are not used elsewhere, they're only for user information.
39 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
41 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
43 // Basically view lines are the line data, while screen lines are shown lines.
50 #define HEADERHEIGHT 10
52 #define IDT_SCROLLTIMER 101
54 CBaseView
* CBaseView::m_pwndLeft
= nullptr;
55 CBaseView
* CBaseView::m_pwndRight
= nullptr;
56 CBaseView
* CBaseView::m_pwndBottom
= nullptr;
57 CLocatorBar
* CBaseView::m_pwndLocator
= nullptr;
58 CLineDiffBar
* CBaseView::m_pwndLineDiffBar
= nullptr;
59 CMFCStatusBar
* CBaseView::m_pwndStatusBar
= nullptr;
60 CMFCRibbonStatusBar
* CBaseView::m_pwndRibbonStatusBar
= nullptr;
61 CMainFrame
* CBaseView::m_pMainFrame
= nullptr;
62 CBaseView::Screen2View
CBaseView::m_Screen2View
;
63 const UINT
CBaseView::m_FindDialogMessage
= RegisterWindowMessage(FINDMSGSTRING
);
65 allviewstate
CBaseView::m_AllState
;
67 IMPLEMENT_DYNCREATE(CBaseView
, CView
)
69 CBaseView::CBaseView()
70 : m_pCacheBitmap(nullptr)
71 , m_pViewData(nullptr)
72 , m_pOtherViewData(nullptr)
73 , m_pOtherView(nullptr)
77 , m_nLastScreenChars(-1)
78 , m_nMaxLineLength(-1)
84 , m_mouseInMargin(false)
86 , m_lineendings(EOL_AUTOLINE
)
88 , m_bReadonlyIsChangable(false)
91 , m_nSelViewBlockStart(-1)
92 , m_nSelViewBlockEnd(-1)
94 , m_bShowSelection(true)
95 , m_texttype(CFileTextLines::AUTOTYPE
)
97 , m_bOtherDiffChecked(false)
98 , m_bInlineWordDiff(true)
99 , m_bWhitespaceInlineDiffs(false)
101 , m_pFindDialog(nullptr)
103 , m_bMatchCase(false)
104 , m_bLimitToDiff(true)
105 , m_bWholeWord(false)
107 , m_pWorkingFile(nullptr)
108 , m_bInsertMode(true)
109 , m_bEditorConfigEnabled(false)
110 , m_bEditorConfigLoaded(2) // 2 = not evaluated
112 m_ptCaretViewPos
.x
= 0;
113 m_ptCaretViewPos
.y
= 0;
114 m_ptSelectionViewPosStart
= m_ptCaretViewPos
;
115 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosStart
;
116 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosEnd
;
117 m_bViewWhitespace
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
118 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
119 m_bShowInlineDiff
= CRegDWORD(L
"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE
);
120 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
121 m_InlineAddedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR
);
122 m_InlineRemovedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR
);
123 m_ModifiedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR
);
124 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW
));
125 m_sWordSeparators
= CRegString(L
"Software\\TortoiseGitMerge\\WordSeparators", L
"[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?");
126 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
127 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
128 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
129 m_bEditorConfigEnabled
= !!static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE
));
130 std::fill_n(m_apFonts
, fontsCount
, static_cast<CFont
*>(nullptr));
132 int cxIcon
= GetSystemMetrics(SM_CXSMICON
);
133 int cyIcon
= GetSystemMetrics(SM_CYSMICON
);
134 m_hConflictedIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDLINE
, cxIcon
, cyIcon
);
135 m_hConflictedIgnoredIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDIGNOREDLINE
, cxIcon
, cyIcon
);
136 m_hRemovedIcon
= CCommonAppUtils::LoadIconEx(IDI_REMOVEDLINE
, cxIcon
, cyIcon
);
137 m_hAddedIcon
= CCommonAppUtils::LoadIconEx(IDI_ADDEDLINE
, cxIcon
, cyIcon
);
138 m_hWhitespaceBlockIcon
= CCommonAppUtils::LoadIconEx(IDI_WHITESPACELINE
, cxIcon
, cyIcon
);
139 m_hEqualIcon
= CCommonAppUtils::LoadIconEx(IDI_EQUALLINE
, cxIcon
, cyIcon
);
140 m_hLineEndingCR
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCR
, cxIcon
, cyIcon
);
141 m_hLineEndingCRLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCRLF
, cxIcon
, cyIcon
);
142 m_hLineEndingLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGLF
, cxIcon
, cyIcon
);
143 m_hEditedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEEDITED
, cxIcon
, cyIcon
);
144 m_hMovedIcon
= CCommonAppUtils::LoadIconEx(IDI_MOVEDLINE
, cxIcon
, cyIcon
);
145 m_hMarkedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEMARKED
, cxIcon
, cyIcon
);
146 m_margincursor
= static_cast<HCURSOR
>(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR
), IMAGE_CURSOR
, 0, 0, LR_DEFAULTSIZE
));
148 for (int i
=0; i
<1024; ++i
)
149 m_sConflictedText
+= L
"??";
150 m_sNoLineNr
.LoadString(IDS_EMPTYLINETT
);
154 SecureZeroMemory(&m_lfBaseFont
, sizeof(m_lfBaseFont
));
157 m_Eols
[EOL_LF
] = L
"\n"; // x0a
158 m_Eols
[EOL_CR
] = L
"\r"; // x0d
159 m_Eols
[EOL_CRLF
] = L
"\r\n"; // x0d x0a
160 m_Eols
[EOL_LFCR
] = L
"\n\r";
161 m_Eols
[EOL_VT
] = L
"\v"; // x0b
162 m_Eols
[EOL_FF
] = L
"\f"; // x0c
163 m_Eols
[EOL_NEL
] = L
"\x85";
164 m_Eols
[EOL_LS
] = L
"\x2028";
165 m_Eols
[EOL_PS
] = L
"\x2029";
166 m_Eols
[EOL_AUTOLINE
] = m_Eols
[m_lineendings
==EOL_AUTOLINE
169 m_SaveParams
.m_LineEndings
= EOL::EOL_AUTOLINE
;
170 m_SaveParams
.m_UnicodeType
= CFileTextLines::AUTOTYPE
;
173 CBaseView::~CBaseView()
177 DestroyIcon(m_hAddedIcon
);
178 DestroyIcon(m_hRemovedIcon
);
179 DestroyIcon(m_hConflictedIcon
);
180 DestroyIcon(m_hConflictedIgnoredIcon
);
181 DestroyIcon(m_hWhitespaceBlockIcon
);
182 DestroyIcon(m_hEqualIcon
);
183 DestroyIcon(m_hLineEndingCR
);
184 DestroyIcon(m_hLineEndingCRLF
);
185 DestroyIcon(m_hLineEndingLF
);
186 DestroyIcon(m_hEditedIcon
);
187 DestroyIcon(m_hMovedIcon
);
188 DestroyIcon(m_hMarkedIcon
);
189 DestroyCursor(m_margincursor
);
192 BEGIN_MESSAGE_MAP(CBaseView
, CView
)
205 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE
, OnMergeNextdifference
)
206 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE
, OnMergePreviousdifference
)
207 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
208 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
211 ON_COMMAND(ID_EDIT_COPY
, OnEditCopy
)
213 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT
, OnMergePreviousconflict
)
214 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT
, OnMergeNextconflict
)
216 ON_COMMAND(ID_CARET_DOWN
, &CBaseView::OnCaretDown
)
217 ON_COMMAND(ID_CARET_LEFT
, &CBaseView::OnCaretLeft
)
218 ON_COMMAND(ID_CARET_RIGHT
, &CBaseView::OnCaretRight
)
219 ON_COMMAND(ID_CARET_UP
, &CBaseView::OnCaretUp
)
220 ON_COMMAND(ID_CARET_WORDLEFT
, &CBaseView::OnCaretWordleft
)
221 ON_COMMAND(ID_CARET_WORDRIGHT
, &CBaseView::OnCaretWordright
)
222 ON_COMMAND(ID_EDIT_CUT
, &CBaseView::OnEditCut
)
223 ON_COMMAND(ID_EDIT_PASTE
, &CBaseView::OnEditPaste
)
225 ON_WM_LBUTTONDBLCLK()
226 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF
, &CBaseView::OnNavigateNextinlinediff
)
227 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF
, &CBaseView::OnNavigatePrevinlinediff
)
228 ON_COMMAND(ID_EDIT_SELECTALL
, &CBaseView::OnEditSelectall
)
229 ON_COMMAND(ID_EDIT_FIND
, OnEditFind
)
230 ON_REGISTERED_MESSAGE(m_FindDialogMessage
, OnFindDialogMessage
)
231 ON_COMMAND(ID_EDIT_FINDNEXT
, OnEditFindnext
)
232 ON_COMMAND(ID_EDIT_FINDPREV
, OnEditFindprev
)
233 ON_COMMAND(ID_EDIT_FINDNEXTSTART
, OnEditFindnextStart
)
234 ON_COMMAND(ID_EDIT_FINDPREVSTART
, OnEditFindprevStart
)
235 ON_COMMAND(ID_EDIT_GOTOLINE
, &CBaseView::OnEditGotoline
)
240 void CBaseView::DocumentUpdated()
246 m_nLastScreenChars
= -1;
247 m_nMaxLineLength
= -1;
251 m_bOtherDiffChecked
= false;
254 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
255 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
256 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
257 m_InlineAddedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineAdded", INLINEADDED_COLOR
);
258 m_InlineRemovedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineRemoved", INLINEREMOVED_COLOR
);
259 m_ModifiedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR
);
260 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\Whitespace", GetSysColor(COLOR_3DSHADOW
));
261 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
262 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
263 m_Eols
[EOL_AUTOLINE
] = m_Eols
[m_lineendings
==EOL_AUTOLINE
266 SetEditorConfigEnabled(m_bEditorConfigEnabled
);
268 ClearCurrentSelection();
273 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled
)
275 m_bEditorConfigEnabled
= bEditorConfigEnabled
;
276 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
277 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
278 if (m_bEditorConfigEnabled
)
280 m_bEditorConfigLoaded
= FALSE
; // no editorconfig entries loaded
281 CEditorConfigWrapper ec
;
282 if (ec
.Load(m_sReflectedName
.IsEmpty() ? m_sFullFilePath
: m_sReflectedName
))
284 m_bEditorConfigLoaded
= TRUE
;
285 if (ec
.m_nTabWidth
!= nullptr)
286 m_nTabSize
= ec
.m_nTabWidth
;
287 if (ec
.m_bIndentStyle
!= nullptr)
288 m_nTabMode
= (m_nTabMode
& ~TABMODE_USESPACES
) | (ec
.m_bIndentStyle
? TABMODE_USESPACES
: TABMODE_NONE
);
293 static CString
GetTabModeString(int nTabMode
, int nTabSize
, bool bEditorConfig
)
296 if (nTabMode
& TABMODE_USESPACES
)
300 text
.AppendFormat(L
" %d", nTabSize
);
301 if (nTabMode
& TABMODE_SMARTINDENT
)
308 void CBaseView::UpdateStatusBar()
310 int nRemovedLines
= 0;
312 int nConflictedLines
= 0;
316 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
318 DiffStates state
= m_pViewData
->GetState(i
);
321 case DIFFSTATE_ADDED
:
322 case DIFFSTATE_IDENTICALADDED
:
323 case DIFFSTATE_THEIRSADDED
:
324 case DIFFSTATE_YOURSADDED
:
325 case DIFFSTATE_CONFLICTADDED
:
328 case DIFFSTATE_IDENTICALREMOVED
:
329 case DIFFSTATE_REMOVED
:
330 case DIFFSTATE_THEIRSREMOVED
:
331 case DIFFSTATE_YOURSREMOVED
:
334 case DIFFSTATE_CONFLICTED
:
335 case DIFFSTATE_CONFLICTED_IGNORED
:
347 sBarText
+= CFileTextLines::GetEncodingName(m_texttype
);
348 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
349 sBarText
+= GetEolName(m_lineendings
);
350 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
352 if (sBarText
.IsEmpty())
358 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
359 if (!sBarText
.IsEmpty())
365 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
366 if (!sBarText
.IsEmpty())
370 if (nConflictedLines
)
372 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
373 if (!sBarText
.IsEmpty())
377 if (m_pwndStatusBar
|| m_pwndRibbonStatusBar
)
384 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
386 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
388 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
390 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
391 sBarText
= sTemp
+sBarText
;
393 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
395 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
396 sBarText
= sTemp
+sBarText
;
398 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
399 m_pwndStatusBar
->GetPaneInfo(nIndex
, nID
, nStyle
, cxWidth
);
400 //calculate the width of the text
401 CDC
* pDC
= m_pwndStatusBar
->GetDC();
404 CSize size
= pDC
->GetTextExtent(sBarText
);
405 m_pwndStatusBar
->SetPaneInfo(nIndex
, nID
, nStyle
, size
.cx
+2);
408 m_pwndStatusBar
->SetPaneText(nIndex
, sBarText
);
410 else if (m_pwndRibbonStatusBar
)
412 if (!IsViewGood(m_pwndBottom
))
413 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
414 if ((m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
) && (IsViewGood(this)))
416 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
417 auto apBtnGroupBottom
= std::make_unique
<CMFCRibbonButtonsGroup
>();
418 apBtnGroupBottom
->SetID(ID_INDICATOR_BOTTOMVIEW
);
419 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR
, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW
)), TRUE
));
420 CMFCRibbonButton
* pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING
, L
"");
421 m_pMainFrame
->FillEncodingButton(pButton
, ID_INDICATOR_BOTTOMENCODINGSTART
);
422 apBtnGroupBottom
->AddButton(pButton
);
423 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL
, L
"");
424 m_pMainFrame
->FillEOLButton(pButton
, ID_INDICATOR_BOTTOMEOLSTART
);
425 apBtnGroupBottom
->AddButton(pButton
);
426 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE
, L
"");
427 m_pMainFrame
->FillTabModeButton(pButton
, ID_INDICATOR_BOTTOMTABMODESTART
);
428 apBtnGroupBottom
->AddButton(pButton
);
429 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW
, L
"", TRUE
));
430 m_pwndRibbonStatusBar
->AddExtendedElement(apBtnGroupBottom
.release(), L
"");
433 CMFCRibbonButtonsGroup
* pGroup
= DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup
, m_pwndRibbonStatusBar
->FindByID(m_nStatusBarID
));
436 CMFCRibbonStatusBarPane
* pPane
= DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane
, pGroup
->GetButton(4));
439 pPane
->SetText(sBarText
);
441 CMFCRibbonButton
* pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(1));
444 pButton
->SetText(CFileTextLines::GetEncodingName(m_texttype
));
445 pButton
->SetDescription(CFileTextLines::GetEncodingName(m_texttype
));
447 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(2));
450 pButton
->SetText(GetEolName(m_lineendings
));
451 pButton
->SetDescription(GetEolName(m_lineendings
));
453 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(3));
456 pButton
->SetText(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
457 pButton
->SetDescription(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
460 m_pwndRibbonStatusBar
->RecalcLayout();
461 m_pwndRibbonStatusBar
->Invalidate();
466 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
468 if (!CView::PreCreateWindow(cs
))
471 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
472 cs
.style
&= ~WS_BORDER
;
473 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
474 ::LoadCursor(nullptr, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+ 1), nullptr);
476 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
477 if (!pParentWnd
|| !pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
479 // View must always create its own scrollbars,
480 // if only it's not used within splitter
481 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
483 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
487 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
494 if (!m_apFonts
[nIndex
])
496 m_apFonts
[nIndex
] = new CFont
;
497 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
498 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
499 m_lfBaseFont
.lfItalic
= static_cast<BYTE
>(bItalic
);
500 m_lfBaseFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\LogFontSize", 10)));
501 wcsncpy_s(m_lfBaseFont
.lfFaceName
, static_cast<LPCTSTR
>(static_cast<CString
>(CRegString(L
"Software\\TortoiseGitMerge\\LogFontName", L
"Consolas"))), _countof(m_lfBaseFont
.lfFaceName
) - 1);
502 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
504 delete m_apFonts
[nIndex
];
505 m_apFonts
[nIndex
] = nullptr;
506 return CView::GetFont();
509 return m_apFonts
[nIndex
];
512 void CBaseView::CalcLineCharDim()
517 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
518 const CSize szCharExt
= pDC
->GetTextExtent(L
"X");
519 pDC
->SelectObject(pOldFont
);
522 m_nLineHeight
= szCharExt
.cy
;
523 if (m_nLineHeight
<= 0)
525 m_nCharWidth
= szCharExt
.cx
;
526 if (m_nCharWidth
<= 0)
530 int CBaseView::GetScreenChars()
532 if (m_nScreenChars
== -1)
535 GetClientRect(&rect
);
536 m_nScreenChars
= (rect
.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL
)) / GetCharWidth();
537 if (m_nScreenChars
< 0)
540 return m_nScreenChars
;
543 int CBaseView::GetAllMinScreenChars() const
545 int nChars
= INT_MAX
;
546 if (IsLeftViewGood())
547 nChars
= std::min
<int>(nChars
, m_pwndLeft
->GetScreenChars());
548 if (IsRightViewGood())
549 nChars
= std::min
<int>(nChars
, m_pwndRight
->GetScreenChars());
550 if (IsBottomViewGood())
551 nChars
= std::min
<int>(nChars
, m_pwndBottom
->GetScreenChars());
552 return (nChars
==INT_MAX
) ? 0 : nChars
;
555 int CBaseView::GetAllMaxLineLength() const
558 if (IsLeftViewGood())
559 nLength
= std::max
<int>(nLength
, m_pwndLeft
->GetMaxLineLength());
560 if (IsRightViewGood())
561 nLength
= std::max
<int>(nLength
, m_pwndRight
->GetMaxLineLength());
562 if (IsBottomViewGood())
563 nLength
= std::max
<int>(nLength
, m_pwndBottom
->GetMaxLineLength());
567 int CBaseView::GetLineHeight()
569 if (m_nLineHeight
== -1)
571 if (m_nLineHeight
<= 0)
573 return m_nLineHeight
;
576 int CBaseView::GetCharWidth()
578 if (m_nCharWidth
== -1)
580 if (m_nCharWidth
<= 0)
585 int CBaseView::GetMaxLineLength()
587 if (m_nMaxLineLength
== -1)
589 m_nMaxLineLength
= 0;
590 int nLineCount
= GetLineCount();
593 m_nMaxLineLength
= GetLineLengthWithTabsConverted(0);
594 return m_nMaxLineLength
;
596 for (int i
=0; i
<nLineCount
; i
++)
598 int nActualLength
= GetLineLengthWithTabsConverted(i
);
599 if (m_nMaxLineLength
< nActualLength
)
600 m_nMaxLineLength
= nActualLength
;
603 return m_nMaxLineLength
;
606 int CBaseView::GetLineLengthWithTabsConverted(int index
)
610 if (m_pViewData
->GetCount() == 0)
612 if (m_Screen2View
.size() <= index
)
615 if (m_pMainFrame
->m_bWrapLines
)
616 sLine
= GetLineChars(index
);
619 int viewLine
= GetViewLineForScreen(index
);
620 sLine
= m_pViewData
->GetLine(viewLine
);
623 const wchar_t* pChar
= sLine
;
624 auto nLineLength
= sLine
.GetLength();
625 for (int i
= 0; i
< nLineLength
; ++i
)
631 // GetTabSize() - 1 because the tabs are already counted
632 nLineLength
= nLineLength
+ (tabCount
* (GetTabSize() - 1));
633 ASSERT(nLineLength
>= 0);
637 int CBaseView::GetLineLength(int index
)
641 if (m_pViewData
->GetCount() == 0)
643 if (m_Screen2View
.size() <= index
)
645 int viewLine
= GetViewLineForScreen(index
);
646 if (m_pMainFrame
->m_bWrapLines
)
648 int nLineLength
= GetLineChars(index
).GetLength();
649 ASSERT(nLineLength
>= 0);
652 int nLineLength
= m_pViewData
->GetLine(viewLine
).GetLength();
653 ASSERT(nLineLength
>= 0);
657 int CBaseView::GetViewLineLength(int nViewLine
) const
661 if (m_pViewData
->GetCount() <= nViewLine
)
663 int nLineLength
= m_pViewData
->GetLine(nViewLine
).GetLength();
664 ASSERT(nLineLength
>= 0);
668 int CBaseView::GetLineCount() const
672 int nLineCount
= m_Screen2View
.size();
673 ASSERT(nLineCount
>= 0);
677 int CBaseView::GetSubLineOffset(int index
)
679 return m_Screen2View
.GetSubLineOffset(index
);
682 CString
CBaseView::GetViewLineChars(int nViewLine
) const
686 if (m_pViewData
->GetCount() <= nViewLine
)
688 return m_pViewData
->GetLine(nViewLine
);
691 CString
CBaseView::GetLineChars(int index
)
695 if (m_pViewData
->GetCount() == 0)
697 if (m_Screen2View
.size() <= index
)
699 int viewLine
= GetViewLineForScreen(index
);
700 if (m_pMainFrame
->m_bWrapLines
)
702 int subLine
= GetSubLineOffset(index
);
705 if (subLine
< CountMultiLines(viewLine
))
707 return m_ScreenedViewLine
[viewLine
].SubLines
[subLine
];
712 return m_pViewData
->GetLine(viewLine
);
715 void CBaseView::CheckOtherView()
717 if (m_bOtherDiffChecked
)
719 // find out what the 'other' file is
720 m_pOtherViewData
= nullptr;
721 m_pOtherView
= nullptr;
722 if (this == m_pwndLeft
&& IsRightViewGood())
724 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
725 m_pOtherView
= m_pwndRight
;
728 if (this == m_pwndRight
&& IsLeftViewGood())
730 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
731 m_pOtherView
= m_pwndLeft
;
734 m_bOtherDiffChecked
= true;
738 void CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
, int & nStartBlock
, int & nEndBlock
)
740 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
743 DiffStates origstate
= viewData
->GetState(nLineIndex
);
745 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
746 nStartBlock
= nLineIndex
;
747 nEndBlock
= nLineIndex
;
748 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
750 DiffStates state
= viewData
->GetState(nStartBlock
- 1);
751 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
753 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
758 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
760 DiffStates state
= viewData
->GetState(nEndBlock
+ 1);
761 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
763 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
770 CString
CBaseView::GetWhitespaceString(CViewData
*viewData
, int nStartBlock
, int nEndBlock
)
772 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
775 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
776 len
+= viewData
->GetLine(i
).GetLength()+2;
779 // do not check for whitespace blocks if the line is too long, because
780 // reserving a lot of memory here takes too much time (performance hog)
781 if (len
> MAX_WHITESPACEBLOCK_SIZE
*256)
783 block
.Preallocate(len
+1);
784 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
786 block
+= viewData
->GetLine(i
);
787 block
+= m_Eols
[viewData
->GetLineEnding(i
)];
792 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
, int& blockstart
, int& blockend
)
798 if (!m_pOtherViewData
)
800 int viewLine
= GetViewLineForScreen(nLineIndex
);
802 (m_pViewData
->GetState(viewLine
) == DIFFSTATE_NORMAL
) &&
803 (m_pOtherViewData
->GetLine(viewLine
) == m_pViewData
->GetLine(viewLine
))
809 // first check whether the line itself only has whitespace changes
810 CString mine
= m_pViewData
->GetLine(viewLine
);
811 CString other
= m_pOtherViewData
->GetLine(min(viewLine
, m_pOtherViewData
->GetCount() - 1));
812 if (mine
.IsEmpty() && other
.IsEmpty())
823 FilterWhitespaces(mine
, other
);
827 int nStartBlock2
, nEndBlock2
;
828 GetWhitespaceBlock(m_pViewData
, viewLine
, blockstart
, blockend
);
829 GetWhitespaceBlock(m_pOtherViewData
, min(viewLine
, m_pOtherViewData
->GetCount() - 1), nStartBlock2
, nEndBlock2
);
830 mine
= GetWhitespaceString(m_pViewData
, blockstart
, blockend
);
835 other
= GetWhitespaceString(m_pOtherViewData
, nStartBlock2
, nEndBlock2
);
836 bIdentical
= mine
== other
;
837 FilterWhitespaces(mine
, other
);
840 return (!mine
.IsEmpty()) && (mine
== other
);
843 bool CBaseView::IsViewLineHidden(int nViewLine
)
845 return IsViewLineHidden(m_pViewData
, nViewLine
);
848 bool CBaseView::IsViewLineHidden(CViewData
* pViewData
, int nViewLine
)
850 return m_pMainFrame
->m_bCollapsed
&& (pViewData
->GetHideState(nViewLine
)!=HIDESTATE_SHOWN
);
853 int CBaseView::GetLineNumber(int index
) const
857 int viewLine
= GetViewLineForScreen(index
);
858 if (m_pViewData
->GetLineNumber(viewLine
)==DIFF_EMPTYLINENUMBER
)
860 return m_pViewData
->GetLineNumber(viewLine
);
863 int CBaseView::GetScreenLines()
865 if (m_nScreenLines
== -1)
868 GetClientRect(&rect
);
869 SCROLLBARINFO sbi
= { sizeof(sbi
) };
870 if (GetScrollBarInfo(OBJID_HSCROLL
, &sbi
))
872 // only use the scroll bar size if the info is correct and the scrollbar is visible
873 // if anything isn't proper, assume the scrollbar has a size of zero
874 // and calculate the screen lines without it.
875 if (!(sbi
.rgstate
[0] & STATE_SYSTEM_INVISIBLE
) && !(sbi
.rgstate
[0] & STATE_SYSTEM_UNAVAILABLE
))
877 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
878 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
879 if (m_nScreenLines
< 0)
881 return m_nScreenLines
;
884 // if the scroll bar is not visible, unavailable or there was an error,
885 // assume the scroll bar height is zero and return the screen lines here.
886 // Of course, that means the cache (using the member variable) won't work
887 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
888 return (rect
.Height() - HEADERHEIGHT
) / GetLineHeight();
890 return m_nScreenLines
;
893 int CBaseView::GetAllMinScreenLines() const
895 int nLines
= INT_MAX
;
896 if (IsLeftViewGood())
897 nLines
= m_pwndLeft
->GetScreenLines();
898 if (IsRightViewGood())
899 nLines
= std::min
<int>(nLines
, m_pwndRight
->GetScreenLines());
900 if (IsBottomViewGood())
901 nLines
= std::min
<int>(nLines
, m_pwndBottom
->GetScreenLines());
902 return (nLines
== INT_MAX
) || (nLines
< 0) ? 0 : nLines
;
905 int CBaseView::GetAllLineCount() const
908 if (IsLeftViewGood())
909 nLines
= m_pwndLeft
->GetLineCount();
910 if (IsRightViewGood())
911 nLines
= std::max
<int>(nLines
, m_pwndRight
->GetLineCount());
912 if (IsBottomViewGood())
913 nLines
= std::max
<int>(nLines
, m_pwndBottom
->GetLineCount());
917 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
919 if (IsLeftViewGood())
920 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
921 if (IsRightViewGood())
922 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
923 if (IsBottomViewGood())
924 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
927 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
930 si
.cbSize
= sizeof(si
);
934 si
.nPos
= m_nTopLine
;
938 EnableScrollBarCtrl(SB_VERT
, TRUE
);
939 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
944 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
946 si
.nMax
= GetAllLineCount();
947 si
.nPage
= GetAllMinScreenLines();
948 si
.nPos
= m_nTopLine
;
950 VERIFY(SetScrollInfo(SB_VERT
, &si
));
953 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
955 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
957 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
959 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
961 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
963 m_pwndLocator
->Invalidate();
966 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
968 // Note we cannot use nPos because of its 16-bit nature
970 si
.cbSize
= sizeof(si
);
972 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
974 int nPageLines
= GetScreenLines();
975 int nLineCount
= GetLineCount();
979 static LONG textwidth
= 0;
980 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
987 nNewTopLine
= nLineCount
- nPageLines
+ 1;
990 nNewTopLine
= m_nTopLine
- 1;
993 nNewTopLine
= m_nTopLine
+ 1;
996 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
999 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
1001 case SB_THUMBPOSITION
:
1002 m_ScrollTool
.Clear();
1003 nNewTopLine
= si
.nTrackPos
;
1007 nNewTopLine
= si
.nTrackPos
;
1008 if (GetFocus() == this)
1011 GetClientRect(&thumbrect
);
1012 ClientToScreen(&thumbrect
);
1015 thumbpoint
.x
= thumbrect
.right
;
1016 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
1017 m_ScrollTool
.Init(&thumbpoint
);
1020 CString sTemp
= sFormat
;
1021 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
1022 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
1024 thumbpoint
.x
-= textwidth
;
1025 int line
= GetLineNumber(nNewTopLine
);
1027 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
1029 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
1036 if (nNewTopLine
< 0)
1038 if (nNewTopLine
>= nLineCount
)
1039 nNewTopLine
= nLineCount
- 1;
1040 ScrollToLine(nNewTopLine
);
1043 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
1045 if (IsLeftViewGood())
1046 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
1047 if (IsRightViewGood())
1048 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
1049 if (IsBottomViewGood())
1050 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
1053 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
1056 si
.cbSize
= sizeof(si
);
1060 si
.nPos
= m_nOffsetChar
;
1064 EnableScrollBarCtrl(SB_HORZ
, !m_pMainFrame
->m_bWrapLines
);
1065 if (!m_pMainFrame
->m_bWrapLines
)
1067 int minScreenChars
= GetAllMinScreenChars();
1068 int maxLineLength
= GetAllMaxLineLength();
1069 if (minScreenChars
>= maxLineLength
&& m_nOffsetChar
> 0)
1074 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
1076 si
.nMax
= m_pMainFrame
->m_bWrapLines
? minScreenChars
: maxLineLength
;
1077 si
.nMax
+= GetMarginWidth()/GetCharWidth();
1078 si
.nPage
= GetScreenChars();
1079 si
.nPos
= m_nOffsetChar
;
1082 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
1085 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
1087 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
1089 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1091 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1093 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1095 m_pwndLocator
->Invalidate();
1098 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
1101 si
.cbSize
= sizeof(si
);
1103 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
1105 int nPageChars
= GetScreenChars();
1106 int nMaxLineLength
= GetMaxLineLength();
1115 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
1118 nNewOffset
= m_nOffsetChar
- 1;
1121 nNewOffset
= m_nOffsetChar
+ 1;
1124 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
1127 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
1129 case SB_THUMBPOSITION
:
1131 nNewOffset
= si
.nTrackPos
;
1137 if (nNewOffset
>= nMaxLineLength
)
1138 nNewOffset
= nMaxLineLength
- 1;
1141 ScrollToChar(nNewOffset
, TRUE
);
1144 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
1146 if (m_nOffsetChar
!= nNewOffsetChar
)
1148 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
1149 m_nOffsetChar
= nNewOffsetChar
;
1151 GetClientRect(&rcScroll
);
1152 rcScroll
.left
+= GetMarginWidth();
1153 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1154 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
1155 // update the view header
1158 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1159 InvalidateRect(&rcScroll
, FALSE
);
1161 if (bTrackScrollBar
)
1162 RecalcHorzScrollBar(TRUE
);
1164 if (m_pwndLineDiffBar
)
1165 m_pwndLineDiffBar
->Invalidate();
1169 void CBaseView::ScrollAllToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/* = TRUE */)
1172 m_pwndLeft
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1174 m_pwndRight
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1176 m_pwndBottom
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1179 void CBaseView::ScrollAllSide(int delta
)
1181 int nNewOffset
= m_nOffsetChar
;
1182 nNewOffset
+= delta
;
1183 int nMaxLineLength
= GetMaxLineLength();
1184 if (nNewOffset
>= nMaxLineLength
)
1185 nNewOffset
= nMaxLineLength
- 1;
1188 ScrollAllToChar(nNewOffset
, TRUE
);
1189 if (m_pwndLineDiffBar
)
1190 m_pwndLineDiffBar
->Invalidate();
1194 void CBaseView::ScrollSide(int delta
)
1196 int nNewOffset
= m_nOffsetChar
;
1197 nNewOffset
+= delta
;
1198 int nMaxLineLength
= GetMaxLineLength();
1199 if (nNewOffset
>= nMaxLineLength
)
1200 nNewOffset
= nMaxLineLength
- 1;
1203 ScrollToChar(nNewOffset
, TRUE
);
1204 if (m_pwndLineDiffBar
)
1205 m_pwndLineDiffBar
->Invalidate();
1209 void CBaseView::ScrollVertical(short zDelta
)
1211 const int nLineCount
= GetLineCount();
1212 int nTopLine
= m_nTopLine
;
1213 nTopLine
-= (zDelta
/30);
1216 if (nTopLine
>= nLineCount
)
1217 nTopLine
= nLineCount
- 1;
1218 ScrollToLine(nTopLine
, TRUE
);
1221 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
1223 if (m_nTopLine
!= nNewTopLine
)
1225 if (nNewTopLine
< 0)
1228 int nScrollLines
= m_nTopLine
- nNewTopLine
;
1230 m_nTopLine
= nNewTopLine
;
1232 GetClientRect(&rcScroll
);
1233 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1234 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
1236 if (bTrackScrollBar
)
1237 RecalcVertScrollBar(TRUE
);
1243 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
1245 pdc
->FillSolidRect(rect
, ::GetSysColor(COLOR_SCROLLBAR
));
1247 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1249 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1250 HICON icon
= nullptr;
1251 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
1252 TScreenedViewLine::EIcon eIcon
= m_ScreenedViewLine
[nViewLine
].eIcon
;
1253 if (eIcon
==TScreenedViewLine::ICN_UNKNOWN
)
1255 DiffStates state
= m_pViewData
->GetState(nViewLine
);
1258 case DIFFSTATE_ADDED
:
1259 case DIFFSTATE_THEIRSADDED
:
1260 case DIFFSTATE_YOURSADDED
:
1261 case DIFFSTATE_IDENTICALADDED
:
1262 case DIFFSTATE_CONFLICTADDED
:
1263 eIcon
= TScreenedViewLine::ICN_ADD
;
1265 case DIFFSTATE_REMOVED
:
1266 case DIFFSTATE_THEIRSREMOVED
:
1267 case DIFFSTATE_YOURSREMOVED
:
1268 case DIFFSTATE_IDENTICALREMOVED
:
1269 eIcon
= TScreenedViewLine::ICN_REMOVED
;
1271 case DIFFSTATE_CONFLICTED
:
1272 eIcon
= TScreenedViewLine::ICN_CONFLICT
;
1274 case DIFFSTATE_CONFLICTED_IGNORED
:
1275 eIcon
= TScreenedViewLine::ICN_CONFLICTIGNORED
;
1277 case DIFFSTATE_EDITED
:
1278 eIcon
= TScreenedViewLine::ICN_EDIT
;
1283 bool bIdentical
= false;
1284 int blockstart
= -1;
1286 if ((state
!= DIFFSTATE_EDITED
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
, blockstart
, blockend
)))
1289 eIcon
= TScreenedViewLine::ICN_SAME
;
1291 eIcon
= TScreenedViewLine::ICN_WHITESPACEDIFF
;
1292 if (((blockstart
>= 0) && (blockend
>= 0)) && (blockstart
< blockend
))
1294 if (nViewLine
> blockstart
)
1295 Invalidate(); // redraw the upper icons since they're now changing
1296 while (blockstart
<= blockend
)
1297 m_ScreenedViewLine
[blockstart
++].eIcon
= eIcon
;
1300 if (m_pViewData
->GetMovedIndex(nViewLine
) >= 0)
1301 eIcon
= TScreenedViewLine::ICN_MOVED
;
1302 if (m_pViewData
->GetMarked(nViewLine
))
1303 eIcon
= TScreenedViewLine::ICN_MARKED
;
1304 m_ScreenedViewLine
[nViewLine
].eIcon
= eIcon
;
1308 case TScreenedViewLine::ICN_UNKNOWN
:
1309 case TScreenedViewLine::ICN_NONE
:
1311 case TScreenedViewLine::ICN_SAME
:
1312 icon
= m_hEqualIcon
;
1314 case TScreenedViewLine::ICN_EDIT
:
1315 icon
= m_hEditedIcon
;
1317 case TScreenedViewLine::ICN_WHITESPACEDIFF
:
1318 icon
= m_hWhitespaceBlockIcon
;
1320 case TScreenedViewLine::ICN_ADD
:
1321 icon
= m_hAddedIcon
;
1323 case TScreenedViewLine::ICN_CONFLICT
:
1324 icon
= m_hConflictedIcon
;
1326 case TScreenedViewLine::ICN_CONFLICTIGNORED
:
1327 icon
= m_hConflictedIgnoredIcon
;
1329 case TScreenedViewLine::ICN_REMOVED
:
1330 icon
= m_hRemovedIcon
;
1332 case TScreenedViewLine::ICN_MOVED
:
1333 icon
= m_hMovedIcon
;
1335 case TScreenedViewLine::ICN_MARKED
:
1336 icon
= m_hMarkedIcon
;
1340 int iconWidth
= GetSystemMetrics(SM_CXSMICON
);
1341 int iconHeight
= GetSystemMetrics(SM_CYSMICON
);
1344 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ 2, rect
.top
+ (rect
.Height() - iconHeight
) / 2, icon
, iconWidth
, iconHeight
, 0, nullptr, DI_NORMAL
);
1346 if ((m_bViewLinenumbers
)&&(m_nDigits
))
1348 int nSubLine
= GetSubLineOffset(nLineIndex
);
1349 bool bIsFirstSubline
= (nSubLine
== 0) || (nSubLine
== -1);
1350 CString sLinenumber
;
1351 if (bIsFirstSubline
)
1353 CString sLinenumberFormat
;
1354 int nLineNumber
= GetLineNumber(nLineIndex
);
1355 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex
)))
1357 // TODO: do not show if there is no number hidden
1358 // TODO: show number if there is only one
1359 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1360 sLinenumber
.Format(sLinenumberFormat
, (m_nDigits
>1) ? L
"↕⁞" : L
"⁞"); // alternative …
1362 else if (nLineNumber
>= 0)
1364 sLinenumberFormat
.Format(L
"%%%dd", m_nDigits
);
1365 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1367 else if (m_pMainFrame
->m_bWrapLines
)
1369 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1370 sLinenumber
.Format(sLinenumberFormat
, L
"·");
1372 if (!sLinenumber
.IsEmpty())
1374 pdc
->SetBkColor(::GetSysColor(COLOR_SCROLLBAR
));
1375 pdc
->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT
));
1377 pdc
->SelectObject(GetFont());
1378 pdc
->ExtTextOut(rect
.left
+ iconWidth
+ 2, rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, nullptr);
1385 int CBaseView::GetMarginWidth()
1387 int marginWidth
= GetSystemMetrics(SM_CXSMICON
) + 2 + 2;
1389 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1393 int nLength
= m_pViewData
->GetCount();
1394 // find out how many digits are needed to show the highest line number
1396 sMax
.Format(L
"%d", nLength
);
1397 m_nDigits
= sMax
.GetLength();
1399 int nWidth
= GetCharWidth();
1400 marginWidth
+= (m_nDigits
* nWidth
) + 2;
1406 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1408 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1409 COLORREF crBk
, crFg
;
1410 if (IsBottomViewGood())
1412 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL
, crBk
, crFg
);
1413 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
1417 DiffStates state
= DIFFSTATE_REMOVED
;
1418 if (this == m_pwndRight
)
1420 state
= DIFFSTATE_ADDED
;
1422 CDiffColors::GetInstance().GetColors(state
, crBk
, crFg
);
1424 pdc
->SetBkColor(crBk
);
1425 pdc
->FillSolidRect(textrect
, crBk
);
1427 pdc
->SetTextColor(crFg
);
1429 pdc
->SelectObject(GetFont(FALSE
, TRUE
));
1434 sViewTitle
= L
"* " + m_sWindowName
;
1438 sViewTitle
= m_sWindowName
;
1440 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1441 if (nStringLength
> rect
.Width())
1443 int offset
= std::min
<int>(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1444 sViewTitle
= m_sWindowName
.Mid(offset
);
1446 pdc
->ExtTextOut(std::max
<int>(rect
.left
+ (rect
.Width()-nStringLength
)/2, 1),
1447 rect
.top
+ (HEADERHEIGHT
/ 2), ETO_CLIPPED
, textrect
, sViewTitle
, nullptr);
1448 if (this->GetFocus() == this)
1449 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1451 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1454 void CBaseView::OnDraw(CDC
* pDC
)
1457 GetClientRect(rcClient
);
1459 int nLineCount
= GetLineCount();
1460 int nLineHeight
= GetLineHeight();
1463 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1464 if (!m_pCacheBitmap
)
1466 m_pCacheBitmap
= new CBitmap
;
1467 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1469 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1471 DrawHeader(pDC
, rcClient
);
1475 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1476 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1477 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1478 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1480 int nCurrentLine
= m_nTopLine
;
1481 bool bBeyondFileLineCached
= false;
1482 while (rcLine
.top
< rcClient
.bottom
)
1484 if (nCurrentLine
< nLineCount
)
1486 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1487 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1488 bBeyondFileLineCached
= false;
1490 else if (!bBeyondFileLineCached
)
1492 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1493 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1494 bBeyondFileLineCached
= true;
1497 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1500 rcLine
.OffsetRect(0, nLineHeight
);
1503 cacheDC
.SelectObject(pOldBitmap
);
1507 bool CBaseView::IsStateConflicted(DiffStates state
)
1511 case DIFFSTATE_CONFLICTED
:
1512 case DIFFSTATE_CONFLICTED_IGNORED
:
1513 case DIFFSTATE_CONFLICTEMPTY
:
1514 case DIFFSTATE_CONFLICTADDED
:
1520 bool CBaseView::IsStateEmpty(DiffStates state
)
1524 case DIFFSTATE_CONFLICTEMPTY
:
1525 case DIFFSTATE_UNKNOWN
:
1526 case DIFFSTATE_EMPTY
:
1532 bool CBaseView::IsStateRemoved(DiffStates state
)
1536 case DIFFSTATE_REMOVED
:
1537 case DIFFSTATE_THEIRSREMOVED
:
1538 case DIFFSTATE_YOURSREMOVED
:
1539 case DIFFSTATE_IDENTICALREMOVED
:
1545 DiffStates
CBaseView::ResolveState(DiffStates state
)
1547 if (IsStateConflicted(state
))
1549 if (state
== DIFFSTATE_CONFLICTEMPTY
)
1550 return DIFFSTATE_CONFLICTRESOLVEDEMPTY
;
1552 return DIFFSTATE_CONFLICTRESOLVED
;
1558 bool CBaseView::IsLineEmpty(int nLineIndex
)
1560 if (m_pViewData
== 0)
1562 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1563 return IsViewLineEmpty(nViewLine
);
1566 bool CBaseView::IsViewLineEmpty(int nViewLine
)
1568 if (m_pViewData
== 0)
1570 const DiffStates state
= m_pViewData
->GetState(nViewLine
);
1571 return IsStateEmpty(state
);
1574 bool CBaseView::IsLineRemoved(int nLineIndex
)
1576 if (m_pViewData
== 0)
1578 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1579 return IsViewLineRemoved(nViewLine
);
1582 bool CBaseView::IsViewLineRemoved(int nViewLine
)
1584 if (m_pViewData
== 0)
1586 const DiffStates state
= m_pViewData
->GetState(nViewLine
);
1587 return IsStateRemoved(state
);
1590 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1592 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1595 COLORREF
CBaseView::InlineViewLineDiffColor(int nViewLine
)
1597 return IsViewLineRemoved(nViewLine
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1600 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1602 if (origin
.x
< (rc
.left
- GetCharWidth() +1))
1604 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< GetLineCount())))
1606 int viewLine
= GetViewLineForScreen(nLineIndex
);
1607 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
1610 HICON hEndingIcon
= nullptr;
1613 case EOL_CR
: hEndingIcon
= m_hLineEndingCR
; break;
1614 case EOL_CRLF
: hEndingIcon
= m_hLineEndingCRLF
; break;
1615 case EOL_LF
: hEndingIcon
= m_hLineEndingLF
; break;
1618 // If EOL style has changed, color end-of-line markers as inline differences.
1620 m_bShowInlineDiff
&& m_pOtherViewData
&&
1621 (viewLine
< m_pOtherViewData
->GetCount()) &&
1622 (ending
!= EOL_NOENDING
) &&
1623 (ending
!= m_pOtherViewData
->GetLineEnding(viewLine
) &&
1624 (m_pOtherViewData
->GetLineEnding(viewLine
) != EOL_NOENDING
))
1627 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1630 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), 0, nullptr, DI_NORMAL
);
1634 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1635 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1636 int yMiddle
= origin
.y
+ rc
.Height()/2;
1637 int xMiddle
= origin
.x
+GetCharWidth()/2;
1638 bool bMultiline
= false;
1639 if ((m_Screen2View
.size() > nLineIndex
+ 1) && (GetViewLineForScreen(nLineIndex
+ 1) == viewLine
))
1641 if (GetLineLength(nLineIndex
+1))
1645 pDC
->MoveTo(origin
.x
, yMiddle
- CDPIAware::Instance().ScaleY(2));
1646 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
- CDPIAware::Instance().ScaleY(2));
1647 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
+ CDPIAware::Instance().ScaleY(2));
1648 pDC
->LineTo(origin
.x
, yMiddle
+ CDPIAware::Instance().ScaleY(2));
1650 else if (GetLineLength(nLineIndex
) == 0)
1653 else if ((nLineIndex
> 0) && (GetViewLineForScreen(nLineIndex
-1) == viewLine
) && (GetLineLength(nLineIndex
) == 0))
1662 // arrow from top to middle+2, then left
1663 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), rc
.top
+ CDPIAware::Instance().ScaleY(1));
1664 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
);
1666 // arrow from right to left
1667 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
);
1668 pDC
->LineTo(origin
.x
, yMiddle
);
1669 pDC
->LineTo(origin
.x
+ CDPIAware::Instance().ScaleX(4), yMiddle
+ CDPIAware::Instance().ScaleY(4));
1670 pDC
->MoveTo(origin
.x
, yMiddle
);
1671 pDC
->LineTo(origin
.x
+ CDPIAware::Instance().ScaleX(4), yMiddle
- CDPIAware::Instance().ScaleY(4));
1674 // from right-upper to left then down
1675 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
- CDPIAware::Instance().ScaleY(2));
1676 pDC
->LineTo(xMiddle
, yMiddle
- CDPIAware::Instance().ScaleY(2));
1677 pDC
->LineTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1678 pDC
->LineTo(xMiddle
+ CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1679 pDC
->MoveTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1680 pDC
->LineTo(xMiddle
- CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1683 // arrow from top to bottom
1684 pDC
->MoveTo(xMiddle
, rc
.top
);
1685 pDC
->LineTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1686 pDC
->LineTo(xMiddle
+ CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1687 pDC
->MoveTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1688 pDC
->LineTo(xMiddle
- CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1690 case EOL_FF
: // Form Feed, U+000C
1691 case EOL_NEL
: // Next Line, U+0085
1692 case EOL_LS
: // Line Separator, U+2028
1693 case EOL_PS
: // Paragraph Separator, U+2029
1694 // draw a horizontal line at the bottom of this line
1695 pDC
->FillSolidRect(rc
.left
, rc
.bottom
-1, rc
.right
, rc
.bottom
, GetSysColor(COLOR_WINDOWTEXT
));
1696 pDC
->MoveTo(origin
.x
+GetCharWidth()-1, rc
.bottom
-GetCharWidth()-2);
1697 pDC
->LineTo(origin
.x
, rc
.bottom
-2);
1698 pDC
->LineTo(origin
.x
+5, rc
.bottom
-2);
1699 pDC
->MoveTo(origin
.x
, rc
.bottom
-2);
1700 pDC
->LineTo(origin
.x
+1, rc
.bottom
-6);
1702 default: // other EOLs
1703 // arrow from top right to bottom left
1704 pDC
->MoveTo(origin
.x
+GetCharWidth()-1, rc
.bottom
-GetCharWidth());
1705 pDC
->LineTo(origin
.x
, rc
.bottom
-1);
1706 pDC
->LineTo(origin
.x
+5, rc
.bottom
-2);
1707 pDC
->MoveTo(origin
.x
, rc
.bottom
-1);
1708 pDC
->LineTo(origin
.x
+1, rc
.bottom
-6);
1714 pDC
->SelectObject(oldpen
);
1718 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1720 if (!m_bShowSelection
)
1725 if (!GetViewSelection(nSelBlockStart
, nSelBlockEnd
))
1728 const int THICKNESS
= 2;
1729 COLORREF rectcol
= GetSysColor(m_bFocused
? COLOR_WINDOWTEXT
: COLOR_GRAYTEXT
);
1731 int nViewLineIndex
= GetViewLineForScreen(nLineIndex
);
1732 int nSubLine
= GetSubLineOffset(nLineIndex
);
1733 bool bFirstLineOfViewLine
= (nSubLine
==0 || nSubLine
==-1);
1734 if ((nViewLineIndex
== nSelBlockStart
) && bFirstLineOfViewLine
)
1736 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1739 bool bLastLineOfViewLine
= (nLineIndex
+1 == m_Screen2View
.size()) || (GetViewLineForScreen(nLineIndex
) != GetViewLineForScreen(nLineIndex
+1));
1740 if ((nViewLineIndex
== nSelBlockEnd
) && bLastLineOfViewLine
)
1742 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1746 void CBaseView::DrawTextLine(
1747 CDC
* pDC
, const CRect
&rc
, int nLineIndex
, POINT
& coords
)
1749 ASSERT(nLineIndex
< GetLineCount());
1750 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1751 ASSERT(m_pViewData
&& (nViewLine
< m_pViewData
->GetCount()));
1753 LineColors lineCols
= GetLineColors(nViewLine
);
1755 CString sViewLine
= GetViewLineChars(nViewLine
);
1757 if (m_bShowSelection
&& HasTextSelection())
1759 // has this line selection ?
1760 if ((m_ptSelectionViewPosStart
.y
<= nViewLine
) && (nViewLine
<= m_ptSelectionViewPosEnd
.y
))
1762 int nViewLineLength
= sViewLine
.GetLength();
1764 // first suppose the whole line is selected
1765 int selectedStart
= 0;
1766 int selectedEnd
= nViewLineLength
;
1768 // the view line is partially selected
1769 if (m_ptSelectionViewPosStart
.y
== nViewLine
)
1771 selectedStart
= m_ptSelectionViewPosStart
.x
;
1774 if (m_ptSelectionViewPosEnd
.y
== nViewLine
)
1776 selectedEnd
= m_ptSelectionViewPosEnd
.x
;
1778 // apply selection coloring
1779 // First enforce start and end point
1780 lineCols
.SplitBlock(selectedStart
);
1781 lineCols
.SplitBlock(selectedEnd
);
1782 // change color of affected parts
1783 long intenseColorScale
= m_bFocused
? 70 : 30;
1784 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(selectedStart
);
1785 for ( ; it
!= lineCols
.end() && it
->first
< selectedEnd
; ++it
)
1787 auto& second
= it
->second
;
1788 COLORREF crBk
= CAppUtils::IntenseColor(intenseColorScale
, second
.background
);
1789 if (second
.shot
== second
.background
)
1791 second
.background
= crBk
;
1792 second
.text
= CAppUtils::IntenseColor(intenseColorScale
, second
.text
);
1797 // TODO: remove duplicate from selection and mark
1798 if (!m_sMarkedWord
.IsEmpty())
1800 int nMarkLength
= m_sMarkedWord
.GetLength();
1801 //int nViewLineLength = sViewLine.GetLength();
1802 const TCHAR
* text
= sViewLine
;
1803 const TCHAR
* findText
= text
;
1804 while ((findText
= wcsstr(findText
, static_cast<LPCTSTR
>(m_sMarkedWord
)))!=0)
1806 int nMarkStart
= static_cast<int>(findText
- text
);
1807 int nMarkEnd
= nMarkStart
+ nMarkLength
;
1808 findText
+= nMarkLength
;
1809 ECharGroup eLeft
= GetCharGroup(sViewLine
, nMarkStart
- 1);
1810 ECharGroup eStart
= GetCharGroup(sViewLine
, nMarkStart
);
1811 if (eLeft
== eStart
)
1813 ECharGroup eRight
= GetCharGroup(sViewLine
, nMarkEnd
);
1814 ECharGroup eEnd
= GetCharGroup(sViewLine
, nMarkEnd
- 1);
1818 // First enforce start and end point
1819 lineCols
.SplitBlock(nMarkStart
);
1820 lineCols
.SplitBlock(nMarkEnd
);
1821 // change color of affected parts
1822 const long int nIntenseColorScale
= 200;
1823 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
);
1824 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
; ++it
)
1826 auto& second
= it
->second
;
1827 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1828 if (second
.shot
== second
.background
)
1830 second
.background
= crBk
;
1831 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1835 if (!m_sFindText
.IsEmpty())
1839 int nStringPos
= nMarkStart
;
1840 CString searchLine
= sViewLine
;
1842 searchLine
.MakeLower();
1843 while (StringFound(searchLine
, SearchNext
, nMarkStart
, nMarkEnd
)!=0)
1845 // First enforce start and end point
1846 lineCols
.SplitBlock(nMarkStart
+nStringPos
);
1847 lineCols
.SplitBlock(nMarkEnd
+nStringPos
);
1848 // change color of affected parts
1849 const long int nIntenseColorScale
= 30;
1850 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
+nStringPos
);
1851 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
; ++it
)
1853 auto& second
= it
->second
;
1854 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1855 if (second
.shot
== second
.background
)
1857 second
.background
= crBk
;
1858 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1860 searchLine
= searchLine
.Mid(nMarkEnd
);
1861 nStringPos
= nMarkEnd
;
1867 // @ this point we may cache data for next line which may be same in wrapped mode
1869 int nTextOffset
= 0;
1870 int nSubline
= GetSubLineOffset(nLineIndex
);
1871 for (int n
=0; n
<nSubline
; n
++)
1873 CString sLine
= m_ScreenedViewLine
[nViewLine
].SubLines
[n
];
1874 nTextOffset
+= sLine
.GetLength();
1877 CString sLine
= GetLineChars(nLineIndex
);
1878 int nLineLength
= sLine
.GetLength();
1879 CString sLineExp
= ExpandChars(sLine
);
1880 LPCTSTR textExp
= sLineExp
;
1881 //int nLineLengthExp = sLineExp.GetLength();
1883 int nLeft
= coords
.x
;
1884 for (std::map
<int, linecolors_t
>::const_iterator itStart
= lineCols
.begin(); itStart
!= lineCols
.end(); ++itStart
)
1886 std::map
<int, linecolors_t
>::const_iterator itEnd
= itStart
;
1888 int nStart
= std::max
<int>(0, itStart
->first
- nTextOffset
);
1889 int nEnd
= nLineLength
;
1890 if (itEnd
!= lineCols
.end())
1892 nEnd
= std::min
<int>(nEnd
, itEnd
->first
- nTextOffset
);
1894 int nBlockLength
= nEnd
- nStart
;
1895 if (nBlockLength
> 0 && nEnd
>=0)
1897 auto& second
= itStart
->second
;
1898 pDC
->SetBkColor(second
.background
);
1899 pDC
->SetTextColor(second
.text
);
1900 int nEndExp
= CountExpandedChars(sLine
, nEnd
);
1901 int nTextLength
= nEndExp
- nStartExp
;
1902 LPCTSTR p_zBlockText
= textExp
+ nStartExp
;
1904 GetTextExtentPoint32(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, &Size
); // falls time-2-tme
1905 int nRight
= nLeft
+ Size
.cx
;
1906 if ((nRight
> rc
.left
) && (nLeft
< rc
.right
))
1908 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1909 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1910 // is 4094 (4095 doesn't work anymore).
1911 // So we limit the length here to that 4094 chars.
1912 // In case we're scrolled to the right, there's no need to draw the string
1913 // from way outside our window, so we also offset the drawing to the start of the window.
1914 // This reduces the string length as well.
1916 int leftcoord
= nLeft
;
1919 int fit
= nTextLength
;
1920 auto posBuffer
= std::make_unique
<int[]>(fit
);
1921 GetTextExtentExPoint(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, INT_MAX
, &fit
, posBuffer
.get(), &Size
);
1922 int lower
= 0, upper
= fit
- 1;
1925 int middle
= (upper
+ lower
+ 1) / 2;
1926 int width
= posBuffer
[middle
];
1927 if (rc
.left
- nLeft
< width
)
1931 } while (lower
< upper
);
1934 nTextLength
-= offset
;
1935 leftcoord
+= lower
> 0 ? posBuffer
[lower
- 1] : 0;
1938 pDC
->ExtTextOut(leftcoord
, coords
.y
, ETO_CLIPPED
, &rc
, p_zBlockText
+ offset
, min(nTextLength
, 4094), nullptr);
1939 if ((second
.shot
!= second
.background
) && (itStart
->first
== nStart
+ nTextOffset
))
1940 pDC
->FillSolidRect(nLeft
- 1, rc
.top
, 1, rc
.Height(), second
.shot
);
1944 nStartExp
= nEndExp
;
1949 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1951 if (nLineIndex
>= GetLineCount())
1953 ASSERT(nLineIndex
>= -1);
1955 if ((nLineIndex
== -1) || !m_pViewData
)
1957 // Draw line beyond the text
1958 COLORREF crBkgnd
, crText
;
1959 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1960 pDC
->FillSolidRect(rc
, crBkgnd
);
1964 int viewLine
= GetViewLineForScreen(nLineIndex
);
1965 if (m_pMainFrame
->m_bCollapsed
)
1967 if (m_pViewData
->GetHideState(viewLine
) == HIDESTATE_MARKER
)
1969 COLORREF crBkgnd
, crText
;
1970 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1971 pDC
->FillSolidRect(rc
, crBkgnd
);
1973 const int THICKNESS
= 2;
1974 COLORREF rectcol
= GetSysColor(COLOR_WINDOWTEXT
);
1975 pDC
->FillSolidRect(rc
.left
, rc
.top
+ (rc
.Height()/2), rc
.Width(), THICKNESS
, rectcol
);
1976 pDC
->SetTextColor(GetSysColor(COLOR_GRAYTEXT
));
1977 pDC
->SetBkColor(crBkgnd
);
1979 pDC
->DrawText(L
"{...}", &rect
, DT_NOPREFIX
|DT_SINGLELINE
|DT_CENTER
);
1984 DiffStates diffState
= m_pViewData
->GetState(viewLine
);
1985 COLORREF crBkgnd
, crText
;
1986 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1988 if (diffState
== DIFFSTATE_CONFLICTED
)
1990 // conflicted lines are shown without 'text' on them
1992 pDC
->FillSolidRect(rc
, crBkgnd
);
1993 // now draw some faint text patterns
1994 pDC
->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd
));
1995 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
1996 DrawBlockLine(pDC
, rc
, nLineIndex
);
2000 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
2001 CString sLine
= GetLineChars(nLineIndex
);
2002 if (sLine
.IsEmpty())
2004 pDC
->FillSolidRect(rc
, crBkgnd
);
2005 DrawBlockLine(pDC
, rc
, nLineIndex
);
2006 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2014 pDC
->SelectObject(GetFont(FALSE
, FALSE
));
2016 DrawTextLine(pDC
, rc
, nLineIndex
, origin
);
2018 // draw white space after the end of line
2020 if (origin
.x
> frect
.left
)
2021 frect
.left
= origin
.x
;
2022 if (frect
.right
> frect
.left
)
2023 pDC
->FillSolidRect(frect
, crBkgnd
);
2025 // draw the whitespace chars
2026 auto pszChars
= static_cast<LPCTSTR
>(sLine
);
2027 if (m_bViewWhitespace
)
2031 LPCTSTR pLastSpace
= pszChars
;
2032 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
2033 xpos
-= m_nOffsetChar
* GetCharWidth();
2035 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
2042 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2043 pLastSpace
= pszChars
+ 1;
2045 int nSpaces
= GetTabSize() - nChars
% GetTabSize();
2046 if (xpos
+ nSpaces
* GetCharWidth() > 0)
2048 int xposreal
= max(xpos
, 0);
2049 if ((xposreal
> 0) || (nSpaces
> 0))
2051 CPen
* oldPen
= pDC
->SelectObject(&pen
);
2052 pDC
->MoveTo(xposreal
+ rc
.left
+ CDPIAware::Instance().ScaleX(2), y
);
2053 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(2), y
);
2054 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(6), y
- CDPIAware::Instance().ScaleY(4));
2055 pDC
->MoveTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(2), y
);
2056 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(6), y
+ CDPIAware::Instance().ScaleY(4));
2057 pDC
->SelectObject(oldPen
);
2060 xpos
+= nSpaces
* GetCharWidth();
2066 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2067 pLastSpace
= pszChars
+ 1;
2070 const int cxWhitespace
= CDPIAware::Instance().ScaleX(2);
2071 const int cyWhitespace
= CDPIAware::Instance().ScaleY(2);
2072 // draw 2-logical pixel rectangle, like Scintilla editor.
2073 pDC
->FillSolidRect(xpos
+ rc
.left
+ GetCharWidth() / 2 - cxWhitespace
/2, y
, cxWhitespace
, cyWhitespace
, m_WhiteSpaceFg
);
2075 xpos
+= GetCharWidth();
2086 DrawBlockLine(pDC
, rc
, nLineIndex
);
2087 if (origin
.x
>= rc
.left
)
2088 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2091 void CBaseView::ExpandChars(const CString
&sLine
, int nOffset
, int nCount
, CString
&line
)
2099 int nTabSize
= GetTabSize();
2101 int nActualOffset
= CountExpandedChars(sLine
, nOffset
);
2103 auto pszChars
= static_cast<LPCTSTR
>(sLine
);
2104 pszChars
+= nOffset
;
2105 int nLength
= nCount
;
2108 for (int i
=0; i
<nLength
; i
++)
2110 if (pszChars
[i
] == L
'\t')
2114 LPTSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
2116 if (nTabCount
> 0 || m_bViewWhitespace
)
2118 for (int i
=0; i
<nLength
; i
++)
2120 if (pszChars
[i
] == L
'\t')
2122 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
2125 pszBuf
[nCurPos
++] = L
' ';
2131 pszBuf
[nCurPos
] = pszChars
[i
];
2138 memcpy(pszBuf
, pszChars
, sizeof(TCHAR
) * nLength
);
2141 pszBuf
[nCurPos
] = 0;
2142 line
.ReleaseBuffer();
2145 CString
CBaseView::ExpandChars(const CString
&sLine
, int nOffset
)
2148 int nLength
= sLine
.GetLength();
2149 ExpandChars(sLine
, nOffset
, nLength
, sRet
);
2153 int CBaseView::CountExpandedChars(const CString
&sLine
, int nLength
)
2155 int nTabSize
= GetTabSize();
2157 int nActualOffset
= 0;
2158 for (int i
=0; i
<nLength
; i
++)
2160 if (sLine
[i
] == L
'\t')
2161 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
2165 return nActualOffset
;
2168 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
2171 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2173 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2175 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2177 m_pwndLocator
->Invalidate();
2180 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
2182 //almost the same as ScrollAllToLine, but try to put the line in the
2183 //middle of the view, not on top
2184 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
2185 if (nNewTopLine
< 0)
2187 if (nNewTopLine
>= m_Screen2View
.size())
2188 nNewTopLine
= m_Screen2View
.size() - 1;
2190 ScrollAllToLine(nNewTopLine
);
2192 ScrollToLine(nNewTopLine
);
2195 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
2200 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
2202 if (CView::OnCreate(lpCreateStruct
) == -1)
2205 SecureZeroMemory(&m_lfBaseFont
, sizeof(m_lfBaseFont
));
2206 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2207 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2208 m_lfBaseFont
.lfHeight
= 0;
2209 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
2210 m_lfBaseFont
.lfItalic
= FALSE
;
2211 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
2212 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
2213 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
2214 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
2215 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
2220 void CBaseView::OnDestroy()
2227 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
2229 CView::OnSize(nType
, cx
, cy
);
2232 m_nScreenLines
= -1;
2233 m_nScreenChars
= -1;
2234 if (m_nLastScreenChars
!= GetScreenChars())
2236 BuildAllScreen2ViewVector();
2237 m_nLastScreenChars
= m_nScreenChars
;
2238 if (m_pMainFrame
&& m_pMainFrame
->m_bWrapLines
)
2240 // if we're in wrap mode, the line wrapping most likely changed
2241 // and that means we have to redraw the whole window, not just the
2247 // make sure the view header is redrawn
2249 GetClientRect(&rcScroll
);
2250 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2251 InvalidateRect(&rcScroll
, FALSE
);
2256 // make sure the view header is redrawn
2258 GetClientRect(&rcScroll
);
2259 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2260 InvalidateRect(&rcScroll
, FALSE
);
2263 RecalcVertScrollBar();
2264 RecalcHorzScrollBar();
2269 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2272 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2274 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2276 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2278 m_pwndLocator
->Invalidate();
2279 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
2282 void CBaseView::OnMouseHWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2285 m_pwndLeft
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2287 m_pwndRight
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2289 m_pwndBottom
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2291 m_pwndLocator
->Invalidate();
2294 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2296 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2297 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2299 if (bControl
|| bShift
)
2301 if (m_pMainFrame
->m_bWrapLines
)
2303 // Ctrl-Wheel scrolls sideways
2304 ScrollSide(-zDelta
/30);
2308 ScrollVertical(zDelta
);
2312 void CBaseView::OnDoMouseHWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2314 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2315 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2317 if (bControl
|| bShift
)
2319 // Ctrl-H-Wheel scrolls vertical
2320 ScrollVertical(zDelta
);
2324 if (m_pMainFrame
->m_bWrapLines
)
2326 // Ctrl-Wheel scrolls sideways
2327 ScrollSide(zDelta
/30);
2331 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
2333 if (nHitTest
== HTCLIENT
)
2335 if ((m_pViewData
)&&(m_pMainFrame
->m_bCollapsed
))
2337 if (m_nMouseLine
< m_Screen2View
.size())
2339 if (m_nMouseLine
>= 0)
2341 int viewLine
= GetViewLineForScreen(m_nMouseLine
);
2342 if (viewLine
< m_pViewData
->GetCount())
2344 if (m_pViewData
->GetHideState(viewLine
) == HIDESTATE_MARKER
)
2346 ::SetCursor(::LoadCursor(nullptr, IDC_HAND
));
2353 if (m_mouseInMargin
)
2355 ::SetCursor(m_margincursor
);
2358 if (m_nMouseLine
>= 0)
2360 ::SetCursor(::LoadCursor(nullptr, IDC_IBEAM
)); // Set To Edit Cursor
2364 ::SetCursor(::LoadCursor(nullptr, IDC_ARROW
)); // Set To Arrow Cursor
2367 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
2370 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
2372 CView::OnKillFocus(pNewWnd
);
2378 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
2380 CView::OnSetFocus(pOldWnd
);
2386 int CBaseView::GetLineFromPoint(CPoint point
)
2388 ScreenToClient(&point
);
2389 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2392 void CBaseView::OnContextMenu(CPoint point
, DiffStates state
)
2395 GetClientRect(rcClient
);
2396 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+ HEADERHEIGHT
);
2398 CRect
borderrect(rcClient
.left
, rcClient
.top
+ m_nLineHeight
+ HEADERHEIGHT
, 0, rcClient
.bottom
);
2400 CPoint ptLocal
= point
;
2401 ScreenToClient(&ptLocal
);
2403 if (textrect
.PtInRect(ptLocal
) || borderrect
.PtInRect(ptLocal
))
2405 // inside the header part of the view (showing the filename)
2406 if (IsViewGood(m_pwndBottom
))
2409 if (this == m_pwndLeft
)
2412 if (!popup
.CreatePopupMenu())
2415 temp
.LoadString(IDS_HEADER_DIFFLEFTTOBASE
);
2416 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2417 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2419 m_pMainFrame
->DiffLeftToBase();
2421 if (this == m_pwndRight
)
2424 if (!popup
.CreatePopupMenu())
2427 temp
.LoadString(IDS_HEADER_DIFFRIGHTTOBASE
);
2428 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2429 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2431 m_pMainFrame
->DiffRightToBase();
2437 if (!this->IsWindowVisible())
2441 if (!popup
.CreatePopupMenu())
2444 AddContextItems(popup
, state
);
2448 int nEncodingCommandBase
= POPUPCOMMAND__LAST
;
2449 int nEolCommandBase
= nEncodingCommandBase
+_countof(uctArray
);
2453 TWhitecharsProperties oWhites
= GetWhitecharsProperties();
2454 temp
.LoadString(IDS_EDIT_TAB2SPACE
);
2455 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTabsToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_TABTOSPACES
, temp
);
2456 temp
.LoadString(IDS_EDIT_SPACE2TAB
);
2457 popup
.AppendMenu(MF_STRING
| (oWhites
.HasSpacesToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_SPACESTOTABS
, temp
);
2458 temp
.LoadString(IDS_EDIT_TRIM
);
2459 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTrailWhiteChars
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_REMOVETRAILWHITES
, temp
);
2462 if (!popupEols
.CreatePopupMenu())
2465 EOL eEolType
= GetLineEndings(oWhites
.HasMixedEols
);
2466 for (int i
= 1; i
< _countof(eolArray
); i
++)
2468 temp
= GetEolName(eolArray
[i
]);
2469 bool bChecked
= (eEolType
== eolArray
[i
]);
2470 popupEols
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEolCommandBase
+i
, temp
);
2473 temp
.LoadString(IDS_VIEWCONTEXTMENU_EOL
);
2474 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupEols
.GetSafeHmenu()), temp
);
2476 // add encoding submenu
2477 if (!popupUnicode
.CreatePopupMenu())
2479 for (int i
= 0; i
< _countof(uctArray
); i
++)
2481 temp
= CFileTextLines::GetEncodingName(uctArray
[i
]);
2482 bool bChecked
= (m_texttype
== uctArray
[i
]);
2483 popupUnicode
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEncodingCommandBase
+i
, temp
);
2485 temp
.LoadString(IDS_VIEWCONTEXTMENU_ENCODING
);
2486 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupUnicode
.GetSafeHmenu()), temp
);
2490 CompensateForKeyboard(point
);
2492 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2494 if (cmd
>= nEncodingCommandBase
&& (cmd
< nEncodingCommandBase
+ static_cast<int>(_countof(uctArray
))))
2496 SetTextType(uctArray
[cmd
-nEncodingCommandBase
]);
2498 if (cmd
>= nEolCommandBase
&& (cmd
< nEolCommandBase
+ static_cast<int>(_countof(eolArray
))))
2500 ReplaceLineEndings(eolArray
[cmd
-nEolCommandBase
]);
2505 // 2-pane view commands; target is right view
2506 case POPUPCOMMAND_USELEFTBLOCK
:
2507 m_pwndRight
->UseLeftBlock();
2509 case POPUPCOMMAND_USELEFTFILE
:
2510 m_pwndRight
->UseLeftFile();
2512 case POPUPCOMMAND_USEBOTHLEFTFIRST
:
2513 m_pwndRight
->UseBothLeftFirst();
2515 case POPUPCOMMAND_USEBOTHRIGHTFIRST
:
2516 m_pwndRight
->UseBothRightFirst();
2518 case POPUPCOMMAND_MARKBLOCK
:
2519 m_pwndRight
->MarkBlock(true);
2521 case POPUPCOMMAND_UNMARKBLOCK
:
2522 m_pwndRight
->MarkBlock(false);
2524 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS
:
2525 m_pwndRight
->LeaveOnlyMarkedBlocks();
2527 // 2-pane view multiedit commands; target is left view
2528 case POPUPCOMMAND_PREPENDFROMRIGHT
:
2529 if (!m_pwndLeft
->IsReadonly())
2530 m_pwndLeft
->UseBothRightFirst();
2532 case POPUPCOMMAND_REPLACEBYRIGHT
:
2533 if (!m_pwndLeft
->IsReadonly())
2534 m_pwndLeft
->UseRightBlock();
2536 case POPUPCOMMAND_APPENDFROMRIGHT
:
2537 if (!m_pwndLeft
->IsReadonly())
2538 m_pwndLeft
->UseBothLeftFirst();
2540 case POPUPCOMMAND_USERIGHTFILE
:
2541 m_pwndLeft
->UseRightFile();
2543 // 3-pane view commands; target is bottom view
2544 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK
:
2545 m_pwndBottom
->UseBothRightFirst();
2547 case POPUPCOMMAND_USETHEIRANDYOURBLOCK
:
2548 m_pwndBottom
->UseBothLeftFirst();
2550 case POPUPCOMMAND_USEYOURBLOCK
:
2551 m_pwndBottom
->UseRightBlock();
2553 case POPUPCOMMAND_USEYOURFILE
:
2554 m_pwndBottom
->UseRightFile();
2556 case POPUPCOMMAND_USETHEIRBLOCK
:
2557 m_pwndBottom
->UseLeftBlock();
2559 case POPUPCOMMAND_USETHEIRFILE
:
2560 m_pwndBottom
->UseLeftFile();
2562 // copy, cut and paste commands
2572 // white chars manipulations
2573 case POPUPCOMMAND_TABTOSPACES
:
2574 ConvertTabToSpaces();
2576 case POPUPCOMMAND_SPACESTOTABS
:
2579 case POPUPCOMMAND_REMOVETRAILWHITES
:
2580 RemoveTrailWhiteChars();
2585 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2589 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
2594 int nViewBlockStart
= -1;
2595 int nViewBlockEnd
= -1;
2596 GetViewSelection(nViewBlockStart
, nViewBlockEnd
);
2597 if ((point
.x
!= -1) && (point
.y
!= -1))
2599 int nLine
= GetLineFromPoint(point
)-1;
2600 if ((nLine
>= 0) && (nLine
< m_Screen2View
.size()))
2602 int nViewLine
= GetViewLineForScreen(nLine
);
2603 if (((nViewLine
< nViewBlockStart
) || (nViewBlockEnd
< nViewLine
)))
2605 ClearSelection(); // Clear text-copy selection
2607 nViewBlockStart
= nViewLine
;
2608 nViewBlockEnd
= nViewLine
;
2609 DiffStates state
= m_pViewData
->GetState(nViewLine
);
2610 while (nViewBlockStart
> 0)
2612 const DiffStates lineState
= m_pViewData
->GetState(nViewBlockStart
-1);
2613 if (!LinesInOneChange(-1, state
, lineState
))
2618 while (nViewBlockEnd
< (m_pViewData
->GetCount()-1))
2620 const DiffStates lineState
= m_pViewData
->GetState(nViewBlockEnd
+1);
2621 if (!LinesInOneChange(1, state
, lineState
))
2626 SetupAllViewSelection(nViewBlockStart
, nViewBlockEnd
);
2627 UpdateCaretPosition(SetupPoint(0, nViewLine
));
2632 // FixSelection(); fix selection range
2633 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2634 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2636 DiffStates state
= DIFFSTATE_UNKNOWN
;
2637 if (GetViewSelection(nViewBlockStart
, nViewBlockEnd
))
2639 // find a more 'relevant' state in the selection
2640 for (int i
=nViewBlockStart
; i
<=nViewBlockEnd
; ++i
)
2642 state
= m_pViewData
->GetState(i
);
2643 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
2647 OnContextMenu(point
, state
);
2650 void CBaseView::RefreshViews()
2654 m_pwndLeft
->UpdateStatusBar();
2655 m_pwndLeft
->UpdateCaret();
2656 m_pwndLeft
->Invalidate();
2660 m_pwndRight
->UpdateStatusBar();
2661 m_pwndRight
->UpdateCaret();
2662 m_pwndRight
->Invalidate();
2666 m_pwndBottom
->UpdateStatusBar();
2667 m_pwndBottom
->UpdateCaret();
2668 m_pwndBottom
->Invalidate();
2671 m_pwndLocator
->Invalidate();
2674 void CBaseView::GoToFirstDifference()
2676 SetCaretToFirstViewLine();
2677 SelectNextBlock(1, false, false);
2680 void CBaseView::GoToFirstConflict()
2682 SetCaretToFirstViewLine();
2683 SelectNextBlock(1, true, false);
2686 void CBaseView::HighlightViewLines(int nStart
, int nEnd
/* = -1 */)
2689 SetupAllViewSelection(nStart
, max(nStart
, nEnd
));
2691 UpdateCaretViewPosition(SetupPoint(0, nStart
));
2695 void CBaseView::SetupAllViewSelection(int start
, int end
)
2697 SetupViewSelection(m_pwndBottom
, start
, end
);
2698 SetupViewSelection(m_pwndLeft
, start
, end
);
2699 SetupViewSelection(m_pwndRight
, start
, end
);
2702 void CBaseView::SetupAllSelection(int start
, int end
)
2704 SetupAllViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2707 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2709 void CBaseView::SetupSelection(int start
, int end
)
2711 SetupViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2714 void CBaseView::SetupViewSelection(CBaseView
* view
, int start
, int end
)
2716 if (!IsViewGood(view
))
2718 view
->SetupViewSelection(start
, end
);
2721 void CBaseView::SetupViewSelection(int start
, int end
)
2723 // clear text selection before setting line selection ?
2724 m_nSelViewBlockStart
= start
;
2725 m_nSelViewBlockEnd
= end
;
2730 void CBaseView::OnMergePreviousconflict()
2732 SelectNextBlock(-1, true);
2735 void CBaseView::OnMergeNextconflict()
2737 SelectNextBlock(1, true);
2740 void CBaseView::OnMergeNextdifference()
2742 SelectNextBlock(1, false);
2745 void CBaseView::OnMergePreviousdifference()
2747 SelectNextBlock(-1, false);
2750 bool CBaseView::HasNextConflict()
2752 return SelectNextBlock(1, true, true, true);
2755 bool CBaseView::HasPrevConflict()
2757 return SelectNextBlock(-1, true, true, true);
2760 bool CBaseView::HasNextDiff()
2762 return SelectNextBlock(1, false, true, true);
2765 bool CBaseView::HasPrevDiff()
2767 return SelectNextBlock(-1, false, true, true);
2770 bool CBaseView::LinesInOneChange(int direction
,
2771 DiffStates initialLineState
, DiffStates currentLineState
)
2773 // Checks whether all the adjacent lines starting from the initial line
2774 // and up to the current line form the single change
2776 // First of all, if the two lines have identical states, they surely
2777 // belong to one change.
2778 if (initialLineState
== currentLineState
)
2781 // Either we move down and initial line state is "added" or "removed" and
2782 // current line state is "empty"...
2785 if (currentLineState
== DIFFSTATE_EMPTY
)
2787 if (initialLineState
== DIFFSTATE_ADDED
|| initialLineState
== DIFFSTATE_REMOVED
)
2790 if (initialLineState
== DIFFSTATE_CONFLICTADDED
&& currentLineState
== DIFFSTATE_CONFLICTEMPTY
)
2793 // ...or we move up and initial line state is "empty" and current line
2794 // state is "added" or "removed".
2797 if (initialLineState
== DIFFSTATE_EMPTY
)
2799 if (currentLineState
== DIFFSTATE_ADDED
|| currentLineState
== DIFFSTATE_REMOVED
)
2802 if (initialLineState
== DIFFSTATE_CONFLICTEMPTY
&& currentLineState
== DIFFSTATE_CONFLICTADDED
)
2808 bool CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */, bool dryrun
/* = false */)
2813 const int linesCount
= m_Screen2View
.size();
2817 int nCenterPos
= GetCaretPosition().y
;
2820 nLimit
= linesCount
;
2822 if (nCenterPos
>= linesCount
)
2823 nCenterPos
= linesCount
-1;
2825 if (bSkipEndOfCurrentBlock
)
2827 // Find end of current block
2828 const DiffStates state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2829 while (nCenterPos
!= nLimit
)
2831 const DiffStates lineState
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2832 if (!LinesInOneChange(nDirection
, state
, lineState
))
2834 nCenterPos
+= nDirection
;
2838 // Find next diff/conflict block
2839 while (nCenterPos
!= nLimit
)
2841 DiffStates linestate
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2843 (linestate
!= DIFFSTATE_NORMAL
) &&
2844 (linestate
!= DIFFSTATE_UNKNOWN
) &&
2845 (linestate
!= DIFFSTATE_FILTEREDDIFF
))
2850 ((linestate
== DIFFSTATE_CONFLICTADDED
) ||
2851 (linestate
== DIFFSTATE_CONFLICTED_IGNORED
) ||
2852 (linestate
== DIFFSTATE_CONFLICTED
) ||
2853 (linestate
== DIFFSTATE_CONFLICTEMPTY
)))
2858 nCenterPos
+= nDirection
;
2860 if (nCenterPos
== nLimit
)
2863 return (nCenterPos
!= nLimit
);
2865 // Find end of new block
2866 DiffStates state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2867 int nBlockEnd
= nCenterPos
;
2868 const int maxAllowedLine
= nLimit
-nDirection
;
2869 while (nBlockEnd
!= maxAllowedLine
)
2871 const int lineIndex
= nBlockEnd
+ nDirection
;
2872 if (lineIndex
>= linesCount
)
2874 DiffStates lineState
= m_pViewData
->GetState(GetViewLineForScreen(lineIndex
));
2875 if (!LinesInOneChange(nDirection
, state
, lineState
))
2877 nBlockEnd
+= nDirection
;
2880 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
2884 POINT ptCaretPos
= {0, nCenterPos
};
2885 SetCaretPosition(ptCaretPos
);
2888 SetupAllSelection(nCenterPos
, nBlockEnd
);
2890 SetupAllSelection(nBlockEnd
, nCenterPos
);
2892 ScrollAllToLine(nTopPos
, FALSE
);
2893 RecalcAllVertScrollBars(TRUE
);
2894 SetCaretToLineStart();
2895 EnsureCaretVisible();
2896 OnNavigateNextinlinediff();
2898 UpdateViewsCaretPosition();
2900 ShowDiffLines(nCenterPos
);
2904 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
2906 if (pNMHDR
->idFrom
!= reinterpret_cast<UINT_PTR
>(m_hWnd
))
2910 strTipText
= m_sWindowName
+ L
"\r\n" + m_sFullFilePath
;
2912 DWORD pos
= GetMessagePos();
2913 CPoint
point(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
));
2914 ScreenToClient(&point
);
2915 const int nLine
= GetButtonEventLineIndex(point
);
2919 int nViewLine
= GetViewLineForScreen(nLine
);
2920 if((m_pViewData
)&&(nViewLine
< m_pViewData
->GetCount()))
2922 auto movedIndex
= m_pViewData
->GetMovedIndex(nViewLine
);
2923 if (movedIndex
>= 0)
2925 if (m_pViewData
->IsMovedFrom(nViewLine
))
2927 strTipText
.Format(IDS_MOVED_TO_TT
, movedIndex
+1);
2931 strTipText
.Format(IDS_MOVED_FROM_TT
, movedIndex
+1);
2939 if (strTipText
.IsEmpty())
2942 // need to handle both ANSI and UNICODE versions of the message
2943 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2945 auto pTTTA
= reinterpret_cast<TOOLTIPTEXTA
*>(pNMHDR
);
2946 pTTTA
->lpszText
= m_szTip
;
2947 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2951 auto pTTTW
= reinterpret_cast<TOOLTIPTEXTW
*>(pNMHDR
);
2952 lstrcpyn(m_wszTip
, strTipText
, min(strTipText
.GetLength() + 1, static_cast<int>(_countof(m_wszTip
)) - 1));
2953 pTTTW
->lpszText
= m_wszTip
;
2956 return TRUE
; // message was handled
2959 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2962 GetClientRect(rcClient
);
2963 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2965 int marginwidth
= GetSystemMetrics(SM_CXSMICON
) + 2 + 2;
2966 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount())&&(m_nDigits
> 0))
2968 marginwidth
+= (m_nDigits
* m_nCharWidth
) + 2;
2970 CRect
borderrect(rcClient
.left
, rcClient
.top
+m_nLineHeight
+HEADERHEIGHT
, marginwidth
, rcClient
.bottom
);
2972 if (textrect
.PtInRect(point
) || borderrect
.PtInRect(point
))
2974 // inside the header part of the view (showing the filename)
2975 pTI
->hwnd
= this->m_hWnd
;
2976 this->GetClientRect(&pTI
->rect
);
2977 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
2978 pTI
->uId
= reinterpret_cast<UINT_PTR
>(m_hWnd
);
2979 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
2981 // we want multi line tooltips
2982 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
2983 if (pToolTip
->GetSafeHwnd())
2984 pToolTip
->SetMaxTipWidth(SHRT_MAX
);
2986 return (textrect
.PtInRect(point
) ? 1 : 2);
2992 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2994 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2995 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3002 if (this==m_pwndLeft
)
3004 if (IsViewGood(m_pwndRight
))
3006 m_pwndRight
->SetFocus();
3008 else if (IsViewGood(m_pwndBottom
))
3010 m_pwndBottom
->SetFocus();
3013 else if (this==m_pwndRight
)
3015 if (IsViewGood(m_pwndBottom
))
3017 m_pwndBottom
->SetFocus();
3019 else if (IsViewGood(m_pwndLeft
))
3021 m_pwndLeft
->SetFocus();
3024 else if (this==m_pwndBottom
)
3026 if (IsViewGood(m_pwndLeft
))
3028 m_pwndLeft
->SetFocus();
3030 else if (IsViewGood(m_pwndRight
))
3032 m_pwndRight
->SetFocus();
3039 POINT ptCaretPos
= GetCaretPosition();
3040 ptCaretPos
.y
-= GetScreenLines();
3041 ptCaretPos
.y
= max(ptCaretPos
.y
, 0l);
3042 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
3043 SetCaretPosition(ptCaretPos
);
3044 OnCaretMove(MOVELEFT
, bShift
);
3045 ShowDiffLines(ptCaretPos
.y
);
3050 POINT ptCaretPos
= GetCaretPosition();
3051 ptCaretPos
.y
+= GetScreenLines();
3052 if (ptCaretPos
.y
>= GetLineCount())
3053 ptCaretPos
.y
= GetLineCount()-1;
3054 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
3055 SetCaretPosition(ptCaretPos
);
3056 OnCaretMove(MOVERIGHT
, bShift
);
3057 ShowDiffLines(ptCaretPos
.y
);
3066 SetCaretToViewStart();
3067 m_nCaretGoalPos
= 0;
3069 AdjustSelection(MOVELEFT
);
3076 POINT ptCaretPos
= GetCaretPosition();
3077 CString sLine
= GetLineChars(ptCaretPos
.y
);
3079 while (pos
< sLine
.GetLength())
3081 if (sLine
[pos
] != ' ' && sLine
[pos
] != '\t')
3085 if (ptCaretPos
.x
== pos
)
3087 SetCaretToLineStart();
3088 m_nCaretGoalPos
= 0;
3089 OnCaretMove(MOVERIGHT
, bShift
);
3095 SetCaretAndGoalPosition(ptCaretPos
);
3096 OnCaretMove(MOVELEFT
, bShift
);
3105 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3107 ptCaretPos
.y
= GetLineCount()-1;
3108 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3109 SetCaretAndGoalPosition(ptCaretPos
);
3111 AdjustSelection(MOVERIGHT
);
3117 POINT ptCaretPos
= GetCaretPosition();
3118 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3119 if ((GetSubLineOffset(ptCaretPos
.y
) != -1) && (GetSubLineOffset(ptCaretPos
.y
) != CountMultiLines(GetViewLineForScreen(ptCaretPos
.y
))-1)) // not last screen line of view line
3123 SetCaretAndGoalPosition(ptCaretPos
);
3124 OnCaretMove(MOVERIGHT
, bShift
);
3131 if (! HasTextSelection())
3133 POINT ptCaretPos
= GetCaretPosition();
3134 if (ptCaretPos
.y
== 0 && ptCaretPos
.x
== 0)
3136 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3138 MoveCaretWordLeft();
3141 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y
))
3145 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3147 RemoveSelectedText();
3153 if (! HasTextSelection())
3157 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3158 MoveCaretWordRight();
3159 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3163 if (! MoveCaretRight())
3165 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3167 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3170 RemoveSelectedText();
3174 m_bInsertMode
= !m_bInsertMode
;
3178 if (bControl
&& m_pwndRight
)
3180 int nFirstViewLine
= 0;
3181 int nLastViewLine
= 0;
3182 if (GetViewSelection(nFirstViewLine
, nLastViewLine
))
3183 m_pwndRight
->MarkBlock(!m_pwndRight
->GetViewMarked(nFirstViewLine
));
3187 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
3190 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
3192 const int nClickedLine
= GetButtonEventLineIndex(point
);
3193 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
3196 ptCaretPos
.y
= nClickedLine
;
3197 int xpos2
= CalcColFromPoint(point
.x
, nClickedLine
);
3198 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, xpos2
);
3199 SetCaretAndGoalPosition(ptCaretPos
);
3201 if (nFlags
& MK_SHIFT
)
3202 AdjustSelection(MOVERIGHT
);
3206 SetupAllSelection(ptCaretPos
.y
, ptCaretPos
.y
);
3207 if (point
.x
< GetMarginWidth())
3209 // select the whole line
3210 m_ptSelectionViewPosStart
= m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3211 m_ptSelectionViewPosStart
.x
= 0;
3212 m_ptSelectionViewPosEnd
.x
= GetViewLineLength(m_ptSelectionViewPosEnd
.y
);
3216 UpdateViewsCaretPosition();
3220 CView::OnLButtonDown(nFlags
, point
);
3223 CBaseView::ECharGroup
CBaseView::GetCharGroup(wchar_t zChar
) const
3225 if (zChar
== ' ' || zChar
== '\t' )
3227 return CHG_WHITESPACE
;
3233 if (m_sWordSeparators
.Find(zChar
) >= 0)
3235 return CHG_WORDSEPARATOR
;
3237 return CHG_WORDLETTER
;
3240 void CBaseView::OnLButtonDblClk(UINT nFlags
, CPoint point
)
3242 if (m_pViewData
== 0) {
3243 CView::OnLButtonDblClk(nFlags
, point
);
3247 const int nClickedLine
= GetButtonEventLineIndex(point
);
3248 if ( nClickedLine
< 0)
3250 int nViewLine
= GetViewLineForScreen(nClickedLine
);
3251 if (point
.x
< GetMarginWidth()) // only if double clicked on the margin
3253 if((nViewLine
< m_pViewData
->GetCount())) // a double click on moved line scrolls to corresponding line
3255 if (m_pViewData
->GetMovedIndex(nViewLine
)>=0)
3257 int movedindex
= m_pViewData
->GetMovedIndex(nViewLine
);
3258 int screenLine
= FindViewLineNumber(movedindex
);
3259 int nTop
= screenLine
- GetScreenLines()/2;
3262 ScrollAllToLine(nTop
);
3263 // find and select the whole moved block
3264 int startSel
= movedindex
;
3265 int endSel
= movedindex
;
3266 while ((startSel
> 0) && (m_pOtherViewData
->GetMovedIndex(startSel
) >= 0))
3269 while ((endSel
< GetLineCount()) && (m_pOtherViewData
->GetMovedIndex(endSel
) >= 0))
3272 m_pOtherView
->SetupSelection(startSel
, endSel
);
3273 return CView::OnLButtonDblClk(nFlags
, point
);
3277 if ((m_pMainFrame
->m_bCollapsed
)&&(m_pViewData
->GetHideState(nViewLine
) == HIDESTATE_MARKER
))
3279 // a double click on a marker expands the hidden text
3281 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) != HIDESTATE_SHOWN
))
3283 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
3284 m_pwndLeft
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3285 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
3286 m_pwndRight
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3287 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
3288 m_pwndBottom
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3291 BuildAllScreen2ViewVector();
3293 m_pwndLeft
->Invalidate();
3295 m_pwndRight
->Invalidate();
3297 m_pwndBottom
->Invalidate();
3302 ptCaretPos
.y
= nClickedLine
;
3303 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3304 SetCaretPosition(ptCaretPos
);
3307 POINT ptViewCarret
= GetCaretViewPosition();
3308 nViewLine
= ptViewCarret
.y
;
3309 if (nViewLine
>= GetViewCount())
3311 const CString
&sLine
= GetViewLine(nViewLine
);
3312 int nLineLength
= sLine
.GetLength();
3313 int nBasePos
= ptViewCarret
.x
;
3314 // get target char group
3315 ECharGroup eLeft
= CHG_UNKNOWN
;
3318 eLeft
= GetCharGroup(sLine
[nBasePos
-1]);
3320 ECharGroup eRight
= CHG_UNKNOWN
;
3321 if (nBasePos
< nLineLength
)
3323 eRight
= GetCharGroup(sLine
[nBasePos
]);
3325 ECharGroup eTarget
= max(eRight
, eLeft
);
3327 int nLeft
= nBasePos
;
3328 while (nLeft
> 0 && GetCharGroup(sLine
[nLeft
-1]) == eTarget
)
3333 int nRight
= nBasePos
;
3334 while (nRight
< nLineLength
&& GetCharGroup(sLine
[nRight
]) == eTarget
)
3339 m_ptSelectionViewPosStart
.x
= nLeft
;
3340 m_ptSelectionViewPosStart
.y
= nViewLine
;
3341 m_ptSelectionViewPosEnd
.x
= nRight
;
3342 m_ptSelectionViewPosEnd
.y
= nViewLine
;
3343 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
3344 SetupAllViewSelection(nViewLine
, nViewLine
);
3346 ptCaretPos
= ConvertViewPosToScreen(m_ptSelectionViewPosEnd
);
3347 UpdateViewsCaretPosition();
3351 m_sPreviousMarkedWord
= m_sMarkedWord
; // store marked word to recall in case of triple click
3352 int nMarkWidth
= max(nRight
- nLeft
, 0);
3353 m_sMarkedWord
= sLine
.Mid(m_ptSelectionViewPosStart
.x
, nMarkWidth
).Trim();
3354 if (m_sMarkedWord
.Compare(m_sPreviousMarkedWord
) == 0)
3356 m_sMarkedWord
.Empty();
3360 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3362 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3364 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3368 m_pwndLocator
->Invalidate();
3371 CView::OnLButtonDblClk(nFlags
, point
);
3374 void CBaseView::OnLButtonTrippleClick( UINT
/*nFlags*/, CPoint point
)
3376 const int nClickedLine
= GetButtonEventLineIndex(point
);
3377 if (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) <= 0)
3379 if (!m_sConvertedFilePath
.IsEmpty() && (GetKeyState(VK_CONTROL
)&0x8000))
3381 PCIDLIST_ABSOLUTE __unaligned pidl
= ILCreateFromPath(static_cast<LPCTSTR
>(m_sConvertedFilePath
));
3384 SHOpenFolderAndSelectItems(pidl
,0,0,0);
3385 CoTaskMemFree((LPVOID
)pidl
);
3391 ptCaretPos
.y
= nClickedLine
;
3392 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3393 SetCaretAndGoalPosition(ptCaretPos
);
3394 m_sMarkedWord
= m_sPreviousMarkedWord
; // recall previous Marked word
3396 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3398 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3400 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3402 m_ptSelectionViewPosStart
.x
= 0;
3403 m_ptSelectionViewPosStart
.y
= nClickedLine
;
3404 m_ptSelectionViewPosEnd
.x
= GetLineLength(nClickedLine
);
3405 m_ptSelectionViewPosEnd
.y
= nClickedLine
;
3406 SetupSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
3407 UpdateViewsCaretPosition();
3410 m_pwndLocator
->Invalidate();
3413 void CBaseView::OnEditCopy()
3415 CString sCopyData
= GetSelectedText();
3417 if (!sCopyData
.IsEmpty())
3419 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
3423 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
3425 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0)
3427 --m_pMainFrame
->m_nMoveMovesToIgnore
;
3428 CView::OnMouseMove(nFlags
, point
);
3431 int nMouseLine
= GetButtonEventLineIndex(point
);
3432 if (nMouseLine
< -1)
3434 m_mouseInMargin
= point
.x
< GetMarginWidth();
3436 ShowDiffLines(nMouseLine
);
3438 KillTimer(IDT_SCROLLTIMER
);
3439 if (nFlags
& MK_LBUTTON
)
3441 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3442 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3443 if (saveMouseLine
< 0)
3445 int col
= CalcColFromPoint(point
.x
, saveMouseLine
);
3446 int charIndex
= CalculateCharIndex(saveMouseLine
, col
);
3447 if (HasSelection() &&
3448 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
3450 POINT ptCaretPos
= {charIndex
, nMouseLine
};
3451 SetCaretAndGoalPosition(ptCaretPos
);
3452 AdjustSelection(MOVERIGHT
);
3456 if (nMouseLine
< m_nTopLine
)
3458 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3459 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3461 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3463 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3464 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3466 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3469 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3471 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3474 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3480 CView::OnMouseMove(nFlags
, point
);
3483 void CBaseView::OnLButtonUp(UINT nFlags
, CPoint point
)
3487 KillTimer(IDT_SCROLLTIMER
);
3489 __super::OnLButtonUp(nFlags
, point
);
3492 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
3494 if (nIDEvent
== IDT_SCROLLTIMER
)
3497 GetCursorPos(&point
);
3498 ScreenToClient(&point
);
3499 int nMouseLine
= GetButtonEventLineIndex(point
);
3500 if (nMouseLine
< -1)
3504 if (GetKeyState(VK_LBUTTON
)&0x8000)
3506 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3507 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3508 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3509 if (nMouseLine
< m_nTopLine
)
3511 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3512 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3514 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3516 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3517 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3519 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3522 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3524 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3527 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3533 CView::OnTimer(nIDEvent
);
3536 void CBaseView::ShowDiffLines(int nLine
)
3538 if ((nLine
< m_nTopLine
)||(nLine
>= GetLineCount()))
3540 m_pwndLineDiffBar
->ShowLines(nLine
);
3542 m_nMouseLine
= nLine
;
3546 if ((!m_pwndRight
)||(!m_pwndLeft
))
3548 if(m_pMainFrame
->m_bOneWay
)
3551 nLine
= (nLine
> m_pwndRight
->m_Screen2View
.size() ? -1 : nLine
);
3552 nLine
= (nLine
> m_pwndLeft
->m_Screen2View
.size() ? -1 : nLine
);
3557 if (nLine
!= m_nMouseLine
)
3559 if (nLine
>= GetLineCount())
3561 m_nMouseLine
= nLine
;
3562 m_pwndLineDiffBar
->ShowLines(nLine
);
3564 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
3567 const viewdata
& CBaseView::GetEmptyLineData()
3569 static const viewdata
emptyLine(L
"", DIFFSTATE_EMPTY
, -1, EOL_NOENDING
, HIDESTATE_SHOWN
);
3573 void CBaseView::InsertViewEmptyLines(int nFirstView
, int nCount
)
3575 for (int i
= 0; i
< nCount
; i
++)
3577 InsertViewData(nFirstView
, GetEmptyLineData());
3582 void CBaseView::UpdateCaret()
3584 POINT ptCaretPos
= GetCaretPosition();
3585 ptCaretPos
.y
= std::max
<int>(std::min
<int>(ptCaretPos
.y
, GetLineCount()-1), 0);
3586 ptCaretPos
.x
= std::max
<int>(std::min
<int>(ptCaretPos
.x
, GetLineLength(ptCaretPos
.y
)), 0);
3587 SetCaretPosition(ptCaretPos
);
3589 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3592 ptCaretPos
.y
>= m_nTopLine
&&
3593 ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
3594 nCaretOffset
>= m_nOffsetChar
&&
3595 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
3597 POINT pt1
= TextToClient(ptCaretPos
);
3599 CreateSolidCaret(2, GetLineHeight());
3602 POINT pt
= { ptCaretPos
.x
+ 1, ptCaretPos
.y
};
3603 POINT pt2
= TextToClient(pt
);
3604 int width
= max(GetCharWidth(), static_cast<int>(pt2
.x
- pt1
.x
));
3605 CreateSolidCaret(width
, GetLineHeight());
3616 POINT
CBaseView::ConvertScreenPosToView(const POINT
& pt
)
3621 int nSubLine
= GetSubLineOffset(pt
.y
);
3624 for (int nScreenLine
= pt
.y
-1; nScreenLine
>= pt
.y
-nSubLine
; nScreenLine
--)
3626 ptViewPos
.x
+= GetLineChars(nScreenLine
).GetLength();
3630 ptViewPos
.y
= GetViewLineForScreen(pt
.y
);
3634 POINT
CBaseView::ConvertViewPosToScreen(const POINT
& pt
)
3637 int nViewLineLenLeft
= GetViewLineLength(pt
.y
);
3638 ptPos
.x
= min(static_cast<LONG
>(nViewLineLenLeft
), pt
.x
);
3639 ptPos
.y
= FindScreenLineForViewLine(pt
.y
);
3640 if (GetViewLineForScreen(ptPos
.y
) != pt
.y
)
3644 else if (GetSubLineOffset(ptPos
.y
) >= 0) // sublined
3646 int nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3647 while (nSubLineLength
< ptPos
.x
)
3649 ptPos
.x
-= nSubLineLength
;
3650 nViewLineLenLeft
-= nSubLineLength
;
3652 nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3654 // last pos of non last sub-line go to start of next screen line
3655 // Note: while this works correctly, it's not what a user might expect:
3656 // cursor-right when the caret is before the last char of a wrapped line
3657 // now moves the caret to the next line. But users expect the caret to
3658 // move to the right of the last char instead, and with another cursor-right
3659 // keystroke to move the caret to the next line.
3660 // Basically, this would require to handle two caret positions for the same
3661 // logical position in the line string (one on the last position of the first line,
3662 // one on the first position of the new line. For non-wrapped lines this works
3663 // because there's an 'invisible' newline char at the end of the first line.
3664 if (nSubLineLength
== ptPos
.x
&& nViewLineLenLeft
> nSubLineLength
)
3675 void CBaseView::EnsureCaretVisible()
3677 POINT ptCaretPos
= GetCaretPosition();
3678 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3680 if (ptCaretPos
.y
< m_nTopLine
)
3681 ScrollAllToLine(ptCaretPos
.y
);
3682 int screnLines
= GetScreenLines();
3685 if (ptCaretPos
.y
>= (m_nTopLine
+screnLines
)-1)
3686 ScrollAllToLine(ptCaretPos
.y
-screnLines
+2);
3687 if (nCaretOffset
< m_nOffsetChar
)
3688 ScrollAllToChar(nCaretOffset
);
3689 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
3690 ScrollAllToChar(nCaretOffset
-GetScreenChars()+1);
3694 int CBaseView::CalculateActualOffset(const POINT
& point
)
3696 int nLineIndex
= point
.y
;
3697 int nCharIndex
= point
.x
;
3698 ASSERT(nCharIndex
>= 0);
3699 CString sLine
= GetLineChars(nLineIndex
);
3700 int nLineLength
= sLine
.GetLength();
3701 return CountExpandedChars(sLine
, min(nCharIndex
, nLineLength
));
3704 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
)
3706 int nLength
= GetLineLength(nLineIndex
);
3707 int nSubLine
= GetSubLineOffset(nLineIndex
);
3710 int nViewLine
= GetViewLineForScreen(nLineIndex
);
3711 if (nViewLine
>= 0 && nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()))
3713 int nMultilineCount
= CountMultiLines(nViewLine
);
3714 if ((nMultilineCount
>0) && (nSubLine
<nMultilineCount
-1))
3720 CString Line
= GetLineChars(nLineIndex
);
3723 int nTabSize
= GetTabSize();
3724 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
3726 if (Line
.GetAt(nIndex
) == L
'\t')
3727 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
3736 * @param xpos X coordinate in CBaseView
3737 * @param lineIndex logical line index (e.g. wrap/collapse)
3739 int CBaseView::CalcColFromPoint(int xpos
, int lineIndex
)
3745 CString text
= ExpandChars(GetLineChars(lineIndex
), 0);
3746 int fit
= text
.GetLength();
3747 auto posBuffer
= std::make_unique
<int[]>(fit
);
3748 pDC
->SelectObject(GetFont()); // is this right font ?
3750 GetTextExtentExPoint(pDC
->GetSafeHdc(), text
, fit
, INT_MAX
, &fit
, posBuffer
.get(), &size
);
3752 int lower
= -1, upper
= fit
- 1;
3753 int xcheck
= xpos
- GetMarginWidth() + m_nOffsetChar
* GetCharWidth();
3756 int middle
= (upper
+ lower
+ 1) / 2;
3757 int width
= posBuffer
[middle
];
3762 } while (lower
< upper
);
3765 if (lower
< fit
- 1)
3767 int charWidth
= posBuffer
[lower
] - (lower
> 0 ? posBuffer
[lower
- 1] : 0);
3768 if (posBuffer
[lower
] - xcheck
<= charWidth
/ 2)
3774 xpos2
= (xpos
- GetMarginWidth()) / GetCharWidth() + m_nOffsetChar
;
3775 if ((xpos
% GetCharWidth()) >= (GetCharWidth()/2))
3781 POINT
CBaseView::TextToClient(const POINT
& point
)
3784 int nOffsetScreenLine
= max(0, static_cast<int>(point
.y
- m_nTopLine
));
3785 pt
.y
= nOffsetScreenLine
* GetLineHeight();
3786 pt
.x
= CalculateActualOffset(point
);
3788 int nLeft
= GetMarginWidth() - m_nOffsetChar
* GetCharWidth();
3789 CDC
* pDC
= GetDC();
3792 pDC
->SelectObject(GetFont()); // is this right font ?
3793 int nScreenLine
= nOffsetScreenLine
+ m_nTopLine
;
3794 CString sLine
= GetLineChars(nScreenLine
);
3795 ExpandChars(sLine
, 0, std::min
<int>(pt
.x
, sLine
.GetLength()), sLine
);
3796 nLeft
+= pDC
->GetTextExtent(sLine
, pt
.x
).cx
;
3799 nLeft
+= pt
.x
* GetCharWidth();
3803 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
3807 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
3809 CView::OnChar(nChar
, nRepCnt
, nFlags
);
3811 bool bControl
= !!(GetKeyState(VK_CONTROL
) & 0x8000);
3812 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3813 bool bSkipSelectionClear
= false;
3818 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
3819 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
3824 if (!m_pViewData
) // no data - nothing to do
3827 if (nChar
== VK_F16
)
3829 // generated by a ctrl+backspace - ignore.
3831 else if (nChar
==VK_TAB
&& HasTextLineSelection())
3833 // change indentation for selected lines
3836 RemoveIndentationForSelectedBlock();
3840 AddIndentationForSelectedBlock();
3842 bSkipSelectionClear
= true;
3844 else if ((nChar
> 31)||(nChar
== VK_TAB
))
3847 RemoveSelectedText();
3848 POINT ptCaretViewPos
= GetCaretViewPosition();
3849 int nViewLine
= ptCaretViewPos
.y
;
3850 if ((nViewLine
==0)&&(GetViewCount()==0))
3851 OnChar(VK_RETURN
, 0, 0);
3853 viewdata lineData
= GetViewData(nViewLine
);
3854 if (nChar
== VK_TAB
)
3856 int indentChars
= GetIndentCharsForLine(ptCaretViewPos
.x
, nViewLine
);
3857 if (indentChars
> 0)
3859 lineData
.sLine
.Insert(ptCaretViewPos
.x
, CString(L
' ', indentChars
));
3860 charCount
= indentChars
;
3863 lineData
.sLine
.Insert(ptCaretViewPos
.x
, L
'\t');
3868 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3871 if (lineData
.sLine
.GetLength() > ptCaretViewPos
.x
)
3872 lineData
.sLine
.SetAt(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3874 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3877 if (IsStateEmpty(lineData
.state
) || IsStateConflicted(lineData
.state
) || lineData
.state
== DIFFSTATE_IDENTICALREMOVED
)
3879 // if not last line set EOL
3880 for (int nCheckViewLine
= nViewLine
+1; nCheckViewLine
< GetViewCount(); nCheckViewLine
++)
3882 if (!IsViewLineEmpty(nCheckViewLine
))
3884 lineData
.ending
= m_lineendings
;
3888 // make sure previous (non empty) line have EOL set
3889 for (int nCheckViewLine
= nViewLine
-1; nCheckViewLine
> 0; nCheckViewLine
--)
3891 if (!IsViewLineEmpty(nCheckViewLine
) && GetViewState(nCheckViewLine
) != DIFFSTATE_IDENTICALREMOVED
)
3893 if (GetViewLineEnding(nCheckViewLine
) == EOL_NOENDING
)
3895 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
3901 lineData
.state
= DIFFSTATE_EDITED
;
3902 bool bNeedRenumber
= false;
3903 if (lineData
.linenumber
== -1)
3905 lineData
.linenumber
= 0;
3906 bNeedRenumber
= true;
3908 SetViewData(nViewLine
, lineData
);
3911 BuildAllScreen2ViewVector(nViewLine
);
3914 UpdateViewLineNumbers();
3916 for (int i
= 0; i
< charCount
; ++i
)
3920 else if (nChar
== 10)
3922 int nViewLine
= GetViewLineForScreen(GetCaretPosition().y
);
3923 EOL eol
= m_pViewData
->GetLineEnding(nViewLine
);
3924 EOL newEOL
= EOL_CRLF
;
3937 if (eol
==EOL_NOENDING
|| eol
==newEOL
)
3938 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3939 // to add EOL on newly edited empty line hit enter
3940 // don't store into UNDO if no change happened
3941 // and don't mark file as modified
3943 AddUndoViewLine(nViewLine
);
3944 m_pViewData
->SetLineEnding(nViewLine
, newEOL
);
3945 m_pViewData
->SetState(nViewLine
, DIFFSTATE_EDITED
);
3948 else if ((nChar
== VK_RETURN
) && !bControl
)
3950 // insert a new, fresh and empty line below the cursor
3951 RemoveSelectedText();
3953 CUndo::GetInstance().BeginGrouping();
3955 POINT ptCaretViewPos
= GetCaretViewPosition();
3956 int nViewLine
= ptCaretViewPos
.y
;
3957 int nLeft
= ptCaretViewPos
.x
;
3958 CString sLine
= GetViewLineChars(nViewLine
);
3959 CString sLineLeft
= sLine
.Left(nLeft
);
3960 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
3961 EOL eOriginalEnding
= EOL_AUTOLINE
;
3962 if (m_pViewData
->GetCount() > nViewLine
)
3963 eOriginalEnding
= GetViewLineEnding(nViewLine
);
3965 if (!sLineRight
.IsEmpty() || (eOriginalEnding
!=m_lineendings
))
3967 viewdata
newFirstLine(sLineLeft
, DIFFSTATE_EDITED
, 1, m_lineendings
, HIDESTATE_SHOWN
);
3968 SetViewData(nViewLine
, newFirstLine
);
3971 int nInsertLine
= (m_pViewData
->GetCount()==0) ? 0 : nViewLine
+ 1;
3972 viewdata
newLastLine(sLineRight
, DIFFSTATE_EDITED
, 1, eOriginalEnding
, HIDESTATE_SHOWN
);
3973 InsertViewData(nInsertLine
, newLastLine
);
3977 // adds new line everywhere except me
3978 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!=this)
3980 m_pwndLeft
->InsertViewEmptyLines(nInsertLine
, 1);
3982 if (IsViewGood(m_pwndRight
) && m_pwndRight
!=this)
3984 m_pwndRight
->InsertViewEmptyLines(nInsertLine
, 1);
3986 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!=this)
3988 m_pwndBottom
->InsertViewEmptyLines(nInsertLine
, 1);
3992 UpdateViewLineNumbers();
3994 CUndo::GetInstance().EndGrouping();
3996 BuildAllScreen2ViewVector();
3997 // move the cursor to the new line
3998 ptCaretViewPos
= SetupPoint(0, nViewLine
+1);
3999 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4002 return; // Unknown control character -- ignore it.
4003 if (!bSkipSelectionClear
)
4005 EnsureCaretVisible();
4010 void CBaseView::AddUndoViewLine(int nViewLine
, bool bAddEmptyLine
)
4013 m_AllState
.left
.AddViewLineFromView(m_pwndLeft
, nViewLine
, bAddEmptyLine
);
4014 m_AllState
.right
.AddViewLineFromView(m_pwndRight
, nViewLine
, bAddEmptyLine
);
4015 m_AllState
.bottom
.AddViewLineFromView(m_pwndBottom
, nViewLine
, bAddEmptyLine
);
4018 RecalcAllVertScrollBars();
4022 void CBaseView::AddEmptyViewLine(int nViewLineIndex
)
4026 int viewLine
= nViewLineIndex
;
4027 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
4028 if (ending
== EOL_NOENDING
)
4030 ending
= m_lineendings
;
4032 viewdata
newLine(L
"", DIFFSTATE_EDITED
, -1, ending
, HIDESTATE_SHOWN
);
4033 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
4035 CString sPartLine
= GetViewLineChars(nViewLineIndex
);
4036 int nPosx
= GetCaretPosition().x
; // should be view pos ?
4037 m_pViewData
->SetLine(viewLine
, sPartLine
.Left(nPosx
));
4038 sPartLine
= sPartLine
.Mid(nPosx
);
4039 newLine
.sLine
= sPartLine
;
4041 m_pViewData
->InsertData(viewLine
+1, newLine
);
4042 BuildAllScreen2ViewVector();
4045 void CBaseView::RemoveSelectedText()
4049 if (!HasTextSelection())
4052 // fix selection if starts or ends on empty line
4053 SetCaretViewPosition(m_ptSelectionViewPosEnd
);
4054 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4057 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
4058 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4059 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4062 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4063 if (!HasTextSelection())
4069 // We want to undo the insertion in a single step.
4071 CUndo::GetInstance().BeginGrouping();
4073 // combine first and last line
4074 viewdata oFirstLine
= GetViewData(m_ptSelectionViewPosStart
.y
);
4075 viewdata oLastLine
= GetViewData(m_ptSelectionViewPosEnd
.y
);
4076 oFirstLine
.sLine
= oFirstLine
.sLine
.Left(m_ptSelectionViewPosStart
.x
) + oLastLine
.sLine
.Mid(m_ptSelectionViewPosEnd
.x
);
4077 oFirstLine
.ending
= oLastLine
.ending
;
4078 oFirstLine
.state
= DIFFSTATE_EDITED
;
4079 SetViewData(m_ptSelectionViewPosStart
.y
, oFirstLine
);
4081 // clean up middle lines if any
4082 if (m_ptSelectionViewPosStart
.y
!= m_ptSelectionViewPosEnd
.y
)
4084 viewdata oEmptyLine
= GetEmptyLineData();
4085 for (int nViewLine
= m_ptSelectionViewPosStart
.y
+1; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
4087 SetViewData(nViewLine
, oEmptyLine
);
4091 if (CleanEmptyLines())
4093 BuildAllScreen2ViewVector(); // schedule full rebuild
4096 UpdateViewLineNumbers();
4099 SetModified(); //TODO set modified only if real data was changed
4101 CUndo::GetInstance().EndGrouping();
4103 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4104 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4108 EnsureCaretVisible();
4112 void CBaseView::PasteText()
4114 if (!OpenClipboard())
4117 CString sClipboardText
;
4118 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
4121 LPCSTR lpstr
= static_cast<LPCSTR
>(GlobalLock(hglb
));
4122 sClipboardText
= CString(lpstr
);
4125 hglb
= GetClipboardData(CF_UNICODETEXT
);
4128 LPCTSTR lpstr
= static_cast<LPCTSTR
>(GlobalLock(hglb
));
4129 sClipboardText
= lpstr
;
4134 if (sClipboardText
.IsEmpty())
4137 sClipboardText
.Replace(L
"\r\n", L
"\r");
4138 sClipboardText
.Replace('\n', '\r');
4140 InsertText(sClipboardText
);
4143 void CBaseView::OnCaretDown()
4145 POINT ptCaretPos
= GetCaretPosition();
4146 int nLine
= ptCaretPos
.y
;
4147 int nNextLine
= nLine
+ 1;
4148 if (nNextLine
>= GetLineCount()) // already at last line
4153 POINT ptCaretViewPos
= GetCaretViewPosition();
4154 int nViewLine
= ptCaretViewPos
.y
;
4155 int nNextViewLine
= GetViewLineForScreen(nNextLine
);
4156 if (!((nNextViewLine
== nViewLine
) && (GetSubLineOffset(nNextLine
)<CountMultiLines(nNextViewLine
)))) // not on same view line
4158 // find next suitable screen line
4159 while ((nNextViewLine
== nViewLine
) || IsViewLineHidden(nNextViewLine
))
4162 if (nNextLine
>= GetLineCount())
4166 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4169 ptCaretPos
.y
= nNextLine
;
4170 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
4171 SetCaretPosition(ptCaretPos
);
4172 OnCaretMove(MOVELEFT
);
4173 ShowDiffLines(ptCaretPos
.y
);
4176 bool CBaseView::MoveCaretLeft()
4178 POINT ptCaretViewPos
= GetCaretViewPosition();
4180 //int nViewLine = ptCaretViewPos.y;
4181 if (ptCaretViewPos
.x
== 0)
4183 int nPrevLine
= GetCaretPosition().y
;
4191 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4192 } while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
));
4193 ptCaretViewPos
= ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine
), nPrevLine
));
4194 ShowDiffLines(nPrevLine
);
4199 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4203 bool CBaseView::MoveCaretRight()
4205 POINT ptCaretViewPos
= GetCaretViewPosition();
4207 int nViewLine
= ptCaretViewPos
.y
;
4208 int nViewLineLen
= GetViewLineLength(nViewLine
);
4209 if (ptCaretViewPos
.x
>= nViewLineLen
)
4211 int nNextLine
= GetCaretPosition().y
;
4215 if (nNextLine
>= GetLineCount())
4219 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4220 } while (nNextViewLine
== nViewLine
|| IsViewLineHidden(nNextViewLine
));
4221 ptCaretViewPos
.y
= nNextViewLine
;
4222 ptCaretViewPos
.x
= 0;
4223 ShowDiffLines(nNextLine
);
4228 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4232 void CBaseView::UpdateGoalPos()
4234 m_nCaretGoalPos
= CalculateActualOffset(GetCaretPosition());
4237 void CBaseView::OnCaretLeft()
4240 OnCaretMove(MOVELEFT
);
4243 void CBaseView::OnCaretRight()
4246 OnCaretMove(MOVERIGHT
);
4249 void CBaseView::OnCaretUp()
4251 POINT ptCaretPos
= GetCaretPosition();
4252 int nLine
= ptCaretPos
.y
;
4253 if (nLine
<= 0) // already at first line
4257 int nPrevLine
= nLine
- 1;
4259 POINT ptCaretViewPos
= GetCaretViewPosition();
4260 int nViewLine
= ptCaretViewPos
.y
;
4261 int nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4262 if (nPrevViewLine
!= nViewLine
) // not on same view line
4264 // find previous suitable screen line
4265 while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
))
4272 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4275 ptCaretPos
.y
= nPrevLine
;
4276 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
4277 SetCaretPosition(ptCaretPos
);
4278 OnCaretMove(MOVELEFT
);
4279 ShowDiffLines(ptCaretPos
.y
);
4282 bool CBaseView::IsWordSeparator(const wchar_t ch
) const
4284 switch (GetCharGroup(ch
))
4287 case CHG_WHITESPACE
:
4288 case CHG_WORDSEPARATOR
:
4294 bool CBaseView::IsCaretAtWordBoundary()
4296 POINT ptViewCaret
= GetCaretViewPosition();
4297 CString line
= GetViewLineChars(ptViewCaret
.y
);
4299 return false; // no boundary at the empty lines
4300 if (ptViewCaret
.x
== 0)
4301 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
));
4302 if (ptViewCaret
.x
>= GetViewLineLength(ptViewCaret
.y
))
4303 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4305 IsWordSeparator(line
.GetAt(ptViewCaret
.x
)) !=
4306 IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4309 void CBaseView::UpdateViewsCaretPosition()
4311 POINT ptCaretPos
= GetCaretPosition();
4312 if (m_pwndBottom
&& m_pwndBottom
!=this)
4313 m_pwndBottom
->UpdateCaretPosition(ptCaretPos
);
4314 if (m_pwndLeft
&& m_pwndLeft
!=this)
4315 m_pwndLeft
->UpdateCaretPosition(ptCaretPos
);
4316 if (m_pwndRight
&& m_pwndRight
!=this)
4317 m_pwndRight
->UpdateCaretPosition(ptCaretPos
);
4320 void CBaseView::OnCaretWordleft()
4322 MoveCaretWordLeft();
4323 OnCaretMove(MOVELEFT
);
4326 void CBaseView::OnCaretWordright()
4328 MoveCaretWordRight();
4329 OnCaretMove(MOVERIGHT
);
4332 void CBaseView::MoveCaretWordLeft()
4334 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4339 void CBaseView::MoveCaretWordRight()
4341 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4346 void CBaseView::ClearCurrentSelection()
4348 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4349 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosStart
;
4350 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
4351 m_nSelViewBlockStart
= -1;
4352 m_nSelViewBlockEnd
= -1;
4356 void CBaseView::ClearSelection()
4359 m_pwndLeft
->ClearCurrentSelection();
4361 m_pwndRight
->ClearCurrentSelection();
4363 m_pwndBottom
->ClearCurrentSelection();
4366 void CBaseView::AdjustSelection(bool bMoveLeft
)
4368 POINT ptCaretViewPos
= GetCaretViewPosition();
4369 if (ArePointsSame(m_ptSelectionViewPosOrigin
, SetupPoint(-1, -1)))
4371 // select all have been used recently update origin
4372 m_ptSelectionViewPosOrigin
= bMoveLeft
? m_ptSelectionViewPosEnd
: m_ptSelectionViewPosStart
;
4374 if ((ptCaretViewPos
.y
< m_ptSelectionViewPosOrigin
.y
) ||
4375 (ptCaretViewPos
.y
== m_ptSelectionViewPosOrigin
.y
&& ptCaretViewPos
.x
<= m_ptSelectionViewPosOrigin
.x
))
4377 m_ptSelectionViewPosStart
= ptCaretViewPos
;
4378 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosOrigin
;
4382 m_ptSelectionViewPosStart
= m_ptSelectionViewPosOrigin
;
4383 m_ptSelectionViewPosEnd
= ptCaretViewPos
;
4386 SetupAllViewSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4391 void CBaseView::OnEditCut()
4396 RemoveSelectedText();
4400 void CBaseView::OnEditPaste()
4404 CUndo::GetInstance().BeginGrouping();
4405 RemoveSelectedText();
4407 CUndo::GetInstance().EndGrouping();
4411 void CBaseView::DeleteFonts()
4413 for (int i
=0; i
<fontsCount
; i
++)
4417 m_apFonts
[i
]->DeleteObject();
4418 delete m_apFonts
[i
];
4419 m_apFonts
[i
] = nullptr;
4424 void CBaseView::OnCaretMove(bool bMoveLeft
)
4426 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
4427 OnCaretMove(bMoveLeft
, bShift
);
4430 void CBaseView::OnCaretMove(bool bMoveLeft
, bool isShiftPressed
)
4433 AdjustSelection(bMoveLeft
);
4436 EnsureCaretVisible();
4440 void CBaseView::AddContextItems(CIconMenu
& popup
, DiffStates
/*state*/)
4442 AddCutCopyAndPaste(popup
);
4445 void CBaseView::AddCutCopyAndPaste(CIconMenu
& popup
)
4447 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4449 temp
.LoadString(IDS_EDIT_COPY
);
4450 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_COPY
, temp
);
4453 temp
.LoadString(IDS_EDIT_CUT
);
4454 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_CUT
, temp
);
4455 temp
.LoadString(IDS_EDIT_PASTE
);
4456 popup
.AppendMenu(MF_STRING
| (CAppUtils::HasClipboardFormat(CF_UNICODETEXT
)||CAppUtils::HasClipboardFormat(CF_TEXT
) ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_PASTE
, temp
);
4457 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4461 void CBaseView::CompensateForKeyboard(CPoint
& point
)
4463 // if the context menu is invoked through the keyboard, we have to use
4464 // a calculated position on where to anchor the menu on
4465 if (ArePointsSame(point
, SetupPoint(-1, -1)))
4468 GetWindowRect(&rect
);
4469 point
= rect
.CenterPoint();
4473 void CBaseView::ReleaseBitmap()
4477 m_pCacheBitmap
->DeleteObject();
4478 delete m_pCacheBitmap
;
4479 m_pCacheBitmap
= nullptr;
4483 void CBaseView::BuildMarkedWordArray()
4485 int lineCount
= GetLineCount();
4486 m_arMarkedWordLines
.clear();
4487 m_arMarkedWordLines
.reserve(lineCount
);
4488 bool bDoit
= !m_sMarkedWord
.IsEmpty();
4489 for (int i
= 0; i
< lineCount
; ++i
)
4493 CString line
= GetLineChars(i
);
4495 if (!line
.IsEmpty())
4498 int nMarkStart
= -1;
4499 while ((nMarkStart
= line
.Find(m_sMarkedWord
, ++nMarkStart
)) >= 0)
4501 int nMarkEnd
= nMarkStart
+ m_sMarkedWord
.GetLength();
4502 ECharGroup eLeft
= GetCharGroup(line
, nMarkStart
- 1);
4503 ECharGroup eStart
= GetCharGroup(line
, nMarkStart
);
4504 if (eLeft
!= eStart
)
4506 ECharGroup eRight
= GetCharGroup(line
, nMarkEnd
);
4507 ECharGroup eEnd
= GetCharGroup(line
, nMarkEnd
- 1);
4515 m_arMarkedWordLines
.push_back(found
);
4518 m_arMarkedWordLines
.push_back(0);
4521 m_arMarkedWordLines
.push_back(0);
4525 void CBaseView::BuildFindStringArray()
4527 int lineCount
= GetLineCount();
4528 m_arFindStringLines
.clear();
4529 m_arFindStringLines
.reserve(lineCount
);
4530 bool bDoit
= !m_sFindText
.IsEmpty();
4533 for (int i
= 0; i
< lineCount
; ++i
)
4537 CString line
= GetLineChars(i
);
4539 if (!line
.IsEmpty())
4541 switch (m_pViewData
->GetState(GetViewLineForScreen(i
)))
4543 case DIFFSTATE_EMPTY
:
4544 m_arFindStringLines
.push_back(0);
4546 case DIFFSTATE_UNKNOWN
:
4547 case DIFFSTATE_NORMAL
:
4548 case DIFFSTATE_FILTEREDDIFF
:
4551 m_arFindStringLines
.push_back(0);
4554 case DIFFSTATE_REMOVED
:
4555 case DIFFSTATE_REMOVEDWHITESPACE
:
4556 case DIFFSTATE_ADDED
:
4557 case DIFFSTATE_ADDEDWHITESPACE
:
4558 case DIFFSTATE_WHITESPACE
:
4559 case DIFFSTATE_WHITESPACE_DIFF
:
4560 case DIFFSTATE_CONFLICTED
:
4561 case DIFFSTATE_CONFLICTED_IGNORED
:
4562 case DIFFSTATE_CONFLICTADDED
:
4563 case DIFFSTATE_CONFLICTEMPTY
:
4564 case DIFFSTATE_CONFLICTRESOLVED
:
4565 case DIFFSTATE_IDENTICALREMOVED
:
4566 case DIFFSTATE_IDENTICALADDED
:
4567 case DIFFSTATE_THEIRSREMOVED
:
4568 case DIFFSTATE_THEIRSADDED
:
4569 case DIFFSTATE_YOURSREMOVED
:
4570 case DIFFSTATE_YOURSADDED
:
4571 case DIFFSTATE_EDITED
:
4574 line
= line
.MakeLower();
4578 while (StringFound(line
, SearchNext
, s
, e
))
4584 m_arFindStringLines
.push_back(match
);
4588 m_arFindStringLines
.push_back(0);
4592 m_arFindStringLines
.push_back(0);
4595 m_arFindStringLines
.push_back(0);
4600 bool CBaseView::GetInlineDiffPositions(int nViewLine
, std::vector
<inlineDiffPos
>& positions
)
4602 if (!m_bShowInlineDiff
)
4604 if (m_pwndBottom
&& !(m_pwndBottom
->IsHidden()))
4607 if (!m_pViewData
|| m_pViewData
->GetCount() <= nViewLine
)
4609 const CString
&sLine
= m_pViewData
->GetLine(nViewLine
);
4610 if (sLine
.IsEmpty())
4614 if (!m_pOtherViewData
)
4617 const CString
&sDiffLine
= m_pOtherViewData
->GetLine(nViewLine
);
4618 if (sDiffLine
.IsEmpty())
4621 svn_diff_t
* diff
= nullptr;
4622 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
4623 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
4624 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
4625 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
4628 size_t lineoffset
= 0;
4629 size_t position
= 0;
4632 if (this == m_pwndRight
)
4634 apr_off_t nTmp
= diff
->modified_length
;
4635 diff
->modified_length
= diff
->original_length
;
4636 diff
->original_length
= nTmp
;
4638 nTmp
= diff
->modified_start
;
4639 diff
->modified_start
= diff
->original_start
;
4640 diff
->original_start
= nTmp
;
4642 apr_off_t len
= diff
->original_length
;
4643 size_t oldpos
= position
;
4645 for (apr_off_t i
= 0; i
< len
; ++i
)
4647 position
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
4651 if (diff
->type
== svn_diff__type_diff_modified
)
4656 positions
.push_back(p
);
4662 return !positions
.empty();
4665 void CBaseView::OnNavigateNextinlinediff()
4668 if (GetNextInlineDiff(nX
))
4670 POINT ptCaretViewPos
= GetCaretViewPosition();
4671 ptCaretViewPos
.x
= nX
;
4672 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4673 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4674 EnsureCaretVisible();
4678 void CBaseView::OnNavigatePrevinlinediff()
4681 if (GetPrevInlineDiff(nX
))
4683 POINT ptCaretViewPos
= GetCaretViewPosition();
4684 ptCaretViewPos
.x
= nX
;
4685 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4686 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4687 EnsureCaretVisible();
4691 bool CBaseView::HasNextInlineDiff()
4694 return GetNextInlineDiff(nPos
);
4697 bool CBaseView::GetNextInlineDiff(int & nPos
)
4699 POINT ptCaretViewPos
= GetCaretViewPosition();
4700 std::vector
<inlineDiffPos
> positions
;
4701 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4703 for (auto it
= positions
.cbegin(); it
!= positions
.cend(); ++it
)
4705 if (it
->start
> ptCaretViewPos
.x
)
4707 nPos
= static_cast<LONG
>(it
->start
);
4710 if (it
->end
> ptCaretViewPos
.x
)
4712 nPos
= static_cast<LONG
>(it
->end
);
4720 bool CBaseView::HasPrevInlineDiff()
4723 return GetPrevInlineDiff(nPos
);
4726 bool CBaseView::GetPrevInlineDiff(int & nPos
)
4728 POINT ptCaretViewPos
= GetCaretViewPosition();
4729 std::vector
<inlineDiffPos
> positions
;
4730 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4732 for (auto it
= positions
.crbegin(); it
!= positions
.crend(); ++it
)
4734 if ( it
->end
< ptCaretViewPos
.x
)
4736 nPos
= static_cast<LONG
>(it
->end
);
4739 if ( it
->start
< ptCaretViewPos
.x
)
4741 nPos
= static_cast<LONG
>(it
->start
);
4749 CBaseView
* CBaseView::GetFirstGoodView()
4751 if (IsViewGood(m_pwndLeft
))
4753 if (IsViewGood(m_pwndRight
))
4755 if (IsViewGood(m_pwndBottom
))
4756 return m_pwndBottom
;
4760 void CBaseView::BuildAllScreen2ViewVector()
4762 CBaseView
* p_pwndView
= GetFirstGoodView();
4765 m_Screen2View
.ScheduleFullRebuild(p_pwndView
->m_pViewData
);
4769 void CBaseView::BuildAllScreen2ViewVector(int nViewLine
)
4771 BuildAllScreen2ViewVector(nViewLine
, nViewLine
);
4774 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine
, int nLastViewLine
)
4776 CBaseView
* p_pwndView
= GetFirstGoodView();
4779 m_Screen2View
.ScheduleRangeRebuild(p_pwndView
->m_pViewData
, nFirstViewLine
, nLastViewLine
);
4783 void CBaseView::UpdateViewLineNumbers()
4785 int nLineNumber
= 0;
4786 int nViewLineCount
= GetViewCount();
4787 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; nViewLine
++)
4789 int oldLine
= GetViewLineNumber(nViewLine
);
4791 SetViewLineNumber(nViewLine
, nLineNumber
++);
4796 int CBaseView::CleanEmptyLines()
4798 int nRemovedCount
= 0;
4799 int nViewLineCount
= GetViewCount();
4800 bool bCheckLeft
= IsViewGood(m_pwndLeft
);
4801 bool bCheckRight
= IsViewGood(m_pwndRight
);
4802 bool bCheckBottom
= IsViewGood(m_pwndBottom
);
4803 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; )
4805 bool bAllEmpty
= true;
4806 bAllEmpty
&= !bCheckLeft
|| IsStateEmpty(m_pwndLeft
->GetViewState(nViewLine
));
4807 bAllEmpty
&= !bCheckRight
|| IsStateEmpty(m_pwndRight
->GetViewState(nViewLine
));
4808 bAllEmpty
&= !bCheckBottom
|| IsStateEmpty(m_pwndBottom
->GetViewState(nViewLine
));
4813 m_pwndLeft
->RemoveViewData(nViewLine
);
4817 m_pwndRight
->RemoveViewData(nViewLine
);
4821 m_pwndBottom
->RemoveViewData(nViewLine
);
4823 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4833 return nRemovedCount
;
4836 int CBaseView::FindScreenLineForViewLine( int viewLine
)
4838 return m_Screen2View
.FindScreenLineForViewLine(viewLine
);
4841 int CBaseView::CountMultiLines( int nViewLine
)
4843 if (m_ScreenedViewLine
.empty())
4844 return 0; // in case the view is completely empty
4846 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4848 if (m_ScreenedViewLine
[nViewLine
].bSublinesSet
)
4850 return static_cast<int>(m_ScreenedViewLine
[nViewLine
].SubLines
.size());
4853 CString multiline
= CStringUtils::WordWrap(m_pViewData
->GetLine(nViewLine
), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4855 TScreenedViewLine oScreenedLine
;
4859 while ((pos
= multiline
.Find('\n', pos
)) >= 0)
4861 oScreenedLine
.SubLines
.push_back(multiline
.Mid(prevpos
, pos
-prevpos
)); // WordWrap could return vector/list of lines instead of string
4865 oScreenedLine
.SubLines
.push_back(multiline
.Mid(prevpos
));
4866 oScreenedLine
.bSublinesSet
= true;
4867 m_ScreenedViewLine
[nViewLine
] = oScreenedLine
;
4869 return CountMultiLines(nViewLine
);
4872 /// prepare inline diff cache
4873 LineColors
& CBaseView::GetLineColors(int nViewLine
)
4875 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4877 if (m_bWhitespaceInlineDiffs
)
4879 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
)
4880 return m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
;
4884 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSet
)
4885 return m_ScreenedViewLine
[nViewLine
].lineColors
;
4888 LineColors oLineColors
;
4889 // set main line color
4890 COLORREF crBkgnd
, crText
;
4891 DiffStates diffState
= m_pViewData
->GetState(nViewLine
);
4892 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
4893 oLineColors
.SetColor(0, crText
, crBkgnd
);
4896 if (!m_bShowInlineDiff
)
4899 if (((diffState
== DIFFSTATE_NORMAL
) || (diffState
== DIFFSTATE_FILTEREDDIFF
)) && (!m_bWhitespaceInlineDiffs
))
4902 CString sLine
= GetViewLineChars(nViewLine
);
4903 if (sLine
.IsEmpty())
4910 case DIFFSTATE_ADDED
:
4912 if ((nViewLine
> 0) && (m_pViewData
->GetState(nViewLine
- 1) == DIFFSTATE_REMOVED
))
4913 sDiffLine
= GetViewLineChars(nViewLine
- 1);
4916 case DIFFSTATE_REMOVED
:
4918 if (((nViewLine
+ 1) < m_pViewData
->GetCount()) && (m_pViewData
->GetState(nViewLine
+ 1) == DIFFSTATE_ADDED
))
4919 sDiffLine
= GetViewLineChars(nViewLine
+ 1);
4925 sDiffLine
= m_pOtherView
->GetViewLineChars(nViewLine
);
4926 if (sDiffLine
.IsEmpty())
4929 svn_diff_t
* diff
= nullptr;
4930 if (sLine
.GetLength() > static_cast<int>(m_nInlineDiffMaxLineLength
))
4932 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
4933 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
4934 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
4935 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
) || !diff
->next
)
4939 int nTextStartOffset
= 0;
4940 std::map
<int, COLORREF
> removedPositions
;
4943 if (this == m_pwndRight
)
4945 apr_off_t nTmp
= diff
->modified_length
;
4946 diff
->modified_length
= diff
->original_length
;
4947 diff
->original_length
= nTmp
;
4949 nTmp
= diff
->modified_start
;
4950 diff
->modified_start
= diff
->original_start
;
4951 diff
->original_start
= nTmp
;
4953 apr_off_t len
= diff
->original_length
;
4955 size_t nTextLength
= 0;
4956 for (int i
= 0; i
< len
; ++i
)
4958 nTextLength
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
4961 bool bInlineDiff
= (diff
->type
== svn_diff__type_diff_modified
);
4963 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
4964 if ((m_bShowInlineDiff
)&&(bInlineDiff
))
4966 crBkgnd
= InlineViewLineDiffColor(nViewLine
);
4968 else if (m_pOtherView
)
4970 crBkgnd
= m_ModifiedBk
;
4973 if (len
< diff
->modified_length
)
4975 removedPositions
[nTextStartOffset
] = m_InlineRemovedBk
;
4977 oLineColors
.SetColor(nTextStartOffset
, crText
, crBkgnd
);
4979 nTextStartOffset
+= static_cast<int>(nTextLength
);
4982 for (std::map
<int, COLORREF
>::const_iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
4984 oLineColors
.AddShotColor(it
->first
, it
->second
);
4986 } while (false); // error catch
4988 if (!m_bWhitespaceInlineDiffs
)
4990 m_ScreenedViewLine
[nViewLine
].lineColors
= oLineColors
;
4991 m_ScreenedViewLine
[nViewLine
].bLineColorsSet
= true;
4995 m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
= oLineColors
;
4996 m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
= true;
4999 return GetLineColors(nViewLine
);
5002 void CBaseView::OnEditSelectall()
5006 int nLastViewLine
= m_pViewData
->GetCount()-1;
5007 if (nLastViewLine
< 0)
5009 SetupAllViewSelection(0, nLastViewLine
);
5011 CString sLine
= GetViewLineChars(nLastViewLine
);
5012 m_ptSelectionViewPosStart
= SetupPoint(0, 0);
5013 m_ptSelectionViewPosEnd
= SetupPoint(sLine
.GetLength(), nLastViewLine
);
5014 m_ptSelectionViewPosOrigin
= SetupPoint(-1, -1);
5019 void CBaseView::FilterWhitespaces(CString
& first
, CString
& second
)
5021 FilterWhitespaces(first
);
5022 FilterWhitespaces(second
);
5025 void CBaseView::FilterWhitespaces(CString
& line
)
5033 int CBaseView::GetButtonEventLineIndex(const POINT
& point
)
5035 const int nLineFromTop
= (point
.y
- HEADERHEIGHT
) / GetLineHeight();
5036 int nEventLine
= nLineFromTop
+ m_nTopLine
;
5037 nEventLine
--; //we need the index
5042 BOOL
CBaseView::PreTranslateMessage(MSG
* pMsg
)
5044 if (RelayTrippleClick(pMsg
))
5046 return CView::PreTranslateMessage(pMsg
);
5050 void CBaseView::ResetUndoStep()
5055 void CBaseView::SaveUndoStep()
5057 if (!m_AllState
.IsEmpty())
5059 CUndo::GetInstance().AddState(m_AllState
, GetCaretViewPosition());
5064 void CBaseView::InsertViewData( int index
, const CString
& sLine
, DiffStates state
, int linenumber
, EOL ending
, HIDESTATE hide
, int movedline
)
5066 m_pState
->addedlines
.push_back(index
);
5067 m_pViewData
->InsertData(index
, sLine
, state
, linenumber
, ending
, hide
, movedline
);
5070 void CBaseView::InsertViewData( int index
, const viewdata
& data
)
5072 m_pState
->addedlines
.push_back(index
);
5073 m_pViewData
->InsertData(index
, data
);
5076 void CBaseView::RemoveViewData( int index
)
5078 m_pState
->removedlines
[index
] = m_pViewData
->GetData(index
);
5079 m_pViewData
->RemoveData(index
);
5082 void CBaseView::SetViewData( int index
, const viewdata
& data
)
5084 m_pState
->replacedlines
[index
] = m_pViewData
->GetData(index
);
5085 m_pViewData
->SetData(index
, data
);
5088 void CBaseView::SetViewState( int index
, DiffStates state
)
5090 m_pState
->linestates
[index
] = m_pViewData
->GetState(index
);
5091 m_pViewData
->SetState(index
, state
);
5094 void CBaseView::SetViewLine( int index
, const CString
& sLine
)
5096 m_pState
->difflines
[index
] = m_pViewData
->GetLine(index
);
5097 m_pViewData
->SetLine(index
, sLine
);
5100 void CBaseView::SetViewLineNumber( int index
, int linenumber
)
5102 int oldLineNumber
= m_pViewData
->GetLineNumber(index
);
5103 if (oldLineNumber
!= linenumber
) {
5104 m_pState
->linelines
[index
] = oldLineNumber
;
5105 m_pViewData
->SetLineNumber(index
, linenumber
);
5109 void CBaseView::SetViewLineEnding( int index
, EOL ending
)
5111 m_pState
->linesEOL
[index
] = m_pViewData
->GetLineEnding(index
);
5112 m_pViewData
->SetLineEnding(index
, ending
);
5115 void CBaseView::SetViewMarked( int index
, bool marked
)
5117 m_pState
->markedlines
[index
] = m_pViewData
->GetMarked(index
);
5118 m_pViewData
->SetMarked(index
, marked
);
5122 BOOL
CBaseView::GetViewSelection( int& start
, int& end
) const
5126 start
= m_nSelViewBlockStart
;
5127 end
= m_nSelViewBlockEnd
;
5133 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine
)
5135 RebuildIfNecessary();
5136 if ((size() <= screenLine
) || (screenLine
< 0))
5138 return m_Screen2View
[screenLine
].nViewLine
;
5141 int CBaseView::Screen2View::size()
5143 RebuildIfNecessary();
5144 return static_cast<int>(m_Screen2View
.size());
5147 int CBaseView::Screen2View::GetSubLineOffset( int screenLine
)
5149 RebuildIfNecessary();
5150 if (size() <= screenLine
)
5152 return m_Screen2View
[screenLine
].nViewSubLine
;
5155 CBaseView::TScreenLineInfo
CBaseView::Screen2View::GetScreenLineInfo( int screenLine
)
5157 RebuildIfNecessary();
5158 return m_Screen2View
[screenLine
];
5162 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5164 void CBaseView::Screen2View::RebuildIfNecessary()
5167 return; // rebuild not necessary
5169 FixScreenedCacheSize(m_pwndLeft
);
5170 FixScreenedCacheSize(m_pwndRight
);
5171 FixScreenedCacheSize(m_pwndBottom
);
5174 for (auto it
= m_RebuildRanges
.cbegin(); it
!= m_RebuildRanges
.cend(); ++it
)
5176 ResetScreenedViewLineCache(m_pwndLeft
, *it
);
5177 ResetScreenedViewLineCache(m_pwndRight
, *it
);
5178 ResetScreenedViewLineCache(m_pwndBottom
, *it
);
5183 ResetScreenedViewLineCache(m_pwndLeft
);
5184 ResetScreenedViewLineCache(m_pwndRight
);
5185 ResetScreenedViewLineCache(m_pwndBottom
);
5187 m_RebuildRanges
.clear();
5190 size_t OldSize
= m_Screen2View
.size();
5191 m_Screen2View
.clear();
5192 m_Screen2View
.reserve(OldSize
); // guess same size
5193 for (int i
= 0; i
< m_pViewData
->GetCount(); ++i
)
5195 if (m_pMainFrame
->m_bCollapsed
)
5197 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) == HIDESTATE_HIDDEN
))
5199 if (!(i
< m_pViewData
->GetCount()))
5202 TScreenLineInfo oLineInfo
;
5203 oLineInfo
.nViewLine
= i
;
5204 oLineInfo
.nViewSubLine
= -1; // no wrap
5205 if (m_pMainFrame
->m_bWrapLines
&& !IsViewLineHidden(m_pViewData
, i
))
5208 if (IsLeftViewGood())
5209 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndLeft
->CountMultiLines(i
));
5210 if (IsRightViewGood())
5211 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndRight
->CountMultiLines(i
));
5212 if (IsBottomViewGood())
5213 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndBottom
->CountMultiLines(i
));
5214 for (int l
= 0; l
< (nMaxLines
-1); ++l
)
5216 oLineInfo
.nViewSubLine
++;
5217 m_Screen2View
.push_back(oLineInfo
);
5219 oLineInfo
.nViewSubLine
++;
5221 m_Screen2View
.push_back(oLineInfo
);
5223 m_pViewData
= nullptr;
5225 if (IsLeftViewGood())
5226 m_pwndLeft
->BuildMarkedWordArray();
5227 if (IsRightViewGood())
5228 m_pwndRight
->BuildMarkedWordArray();
5229 if (IsBottomViewGood())
5230 m_pwndBottom
->BuildMarkedWordArray();
5232 RecalcAllVertScrollBars();
5233 RecalcAllHorzScrollBars();
5236 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine
)
5238 RebuildIfNecessary();
5240 int nScreenLineCount
= static_cast<int>(m_Screen2View
.size());
5243 if (nScreenLineCount
>16)
5245 // for enough long data search for last screen
5246 // with viewline less than one we are looking for
5247 // use approximate method (based on) binary search using asymmetric start point
5248 // in form 2**n (determined as MSB of length) to go around division and rounding;
5249 // this effectively looks for bit values from MSB to LSB
5252 //GetMostSignificantBitValue
5253 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5254 nTestBit
= nScreenLineCount
;
5255 nTestBit
|= nTestBit
>>1;
5256 nTestBit
|= nTestBit
>>2;
5257 nTestBit
|= nTestBit
>>4;
5258 nTestBit
|= nTestBit
>>8;
5259 nTestBit
|= nTestBit
>>16;
5260 nTestBit
^= (nTestBit
>>1);
5264 int nTestPos
= nPos
| nTestBit
;
5265 if (nTestPos
< nScreenLineCount
&& m_Screen2View
[nTestPos
].nViewLine
< viewLine
)
5272 while (nPos
< nScreenLineCount
&& m_Screen2View
[nPos
].nViewLine
< viewLine
)
5280 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData
* pViewData
) {
5283 m_pViewData
= pViewData
;
5286 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData
* pViewData
, int nFirstViewLine
, int nLastViewLine
)
5291 m_pViewData
= pViewData
;
5293 TRebuildRange Range
;
5294 Range
.FirstViewLine
=nFirstViewLine
;
5295 Range
.LastViewLine
=nLastViewLine
;
5296 m_RebuildRanges
.push_back(Range
);
5299 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView
* pwndView
)
5301 if (!IsViewGood(pwndView
))
5305 const int nOldSize
= static_cast<int>(pwndView
->m_ScreenedViewLine
.size());
5306 const int nViewCount
= std::max
<int>(pwndView
->GetViewCount(), 0);
5307 if (nOldSize
== nViewCount
)
5311 pwndView
->m_ScreenedViewLine
.resize(nViewCount
);
5315 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
) const
5317 if (!IsViewGood(pwndView
))
5321 TRebuildRange Range
={0, pwndView
->GetViewCount()-1};
5322 ResetScreenedViewLineCache(pwndView
, Range
);
5326 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
, const TRebuildRange
& Range
) const
5328 if (!IsViewGood(pwndView
))
5332 if (Range
.LastViewLine
== -1)
5336 ASSERT(Range
.FirstViewLine
>= 0);
5337 ASSERT(Range
.LastViewLine
< pwndView
->GetViewCount());
5338 for (int i
= Range
.FirstViewLine
; i
<= Range
.LastViewLine
; i
++)
5340 pwndView
->m_ScreenedViewLine
[i
].Clear();
5345 void CBaseView::WrapChanged()
5347 m_nMaxLineLength
= -1;
5349 RecalcHorzScrollBar();
5352 void CBaseView::OnEditFind()
5358 if (this == m_pwndLeft
)
5360 if (this == m_pwndRight
)
5362 if (this == m_pwndBottom
)
5365 m_pFindDialog
= new CFindDlg(this);
5366 m_pFindDialog
->Create(this, id
);
5368 m_pFindDialog
->SetFindString(HasTextSelection() ? GetSelectedText() : L
"");
5369 m_pFindDialog
->SetReadonly(m_bReadonly
);
5372 LRESULT
CBaseView::OnFindDialogMessage(WPARAM wParam
, LPARAM
/*lParam*/)
5374 ASSERT(m_pFindDialog
!= nullptr);
5376 if (m_pFindDialog
->IsTerminating())
5378 // invalidate the handle identifying the dialog box.
5379 m_pFindDialog
= nullptr;
5383 if(m_pFindDialog
->FindNext())
5385 //read data from dialog
5386 m_sFindText
= m_pFindDialog
->GetFindString();
5387 m_bMatchCase
= (m_pFindDialog
->MatchCase() == TRUE
);
5388 m_bLimitToDiff
= m_pFindDialog
->LimitToDiffs();
5389 m_bWholeWord
= m_pFindDialog
->WholeWord();
5392 m_sFindText
= m_sFindText
.MakeLower();
5394 BuildFindStringArray();
5395 if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Find
)
5397 if (m_pFindDialog
->SearchUp())
5402 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Count
)
5405 for (size_t i
= 0; i
< m_arFindStringLines
.size(); ++i
)
5406 count
+= m_arFindStringLines
[i
];
5408 matches
.Format(IDS_FIND_COUNT
, count
);
5409 m_pFindDialog
->SetStatusText(matches
);
5411 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Replace
)
5415 bool bFound
= false;
5416 if (m_pFindDialog
->SearchUp())
5417 bFound
= Search(SearchPrevious
, true, true, false);
5419 bFound
= Search(SearchNext
, true, true, false);
5422 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5423 CUndo::GetInstance().BeginGrouping();
5424 RemoveSelectedText();
5425 InsertText(sReplaceText
);
5426 CUndo::GetInstance().EndGrouping();
5430 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::ReplaceAll
)
5434 bool bFound
= false;
5435 int replaceCount
= 0;
5436 POINT lastPoint
= m_ptSelectionViewPosStart
;
5437 m_ptSelectionViewPosStart
.x
= m_ptSelectionViewPosStart
.y
= 0;
5438 CUndo::GetInstance().BeginGrouping();
5441 bFound
= Search(SearchNext
, true, false, true);
5444 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5445 RemoveSelectedText();
5446 InsertText(sReplaceText
);
5450 CUndo::GetInstance().EndGrouping();
5451 if (replaceCount
== 0)
5452 m_ptSelectionViewPosStart
= lastPoint
;
5454 message
.Format(IDS_FIND_REPLACED
, replaceCount
);
5455 m_pFindDialog
->SetStatusText(message
, RGB(0, 0, 0));
5462 void CBaseView::OnEditFindnextStart()
5466 if (HasTextSelection())
5468 m_sFindText
= GetSelectedText();
5469 m_bMatchCase
= false;
5470 m_bLimitToDiff
= false;
5471 m_bWholeWord
= false;
5472 m_sFindText
= m_sFindText
.MakeLower();
5474 BuildFindStringArray();
5479 m_sFindText
.Empty();
5480 BuildFindStringArray();
5484 void CBaseView::OnEditFindprevStart()
5488 if (HasTextSelection())
5490 m_sFindText
= GetSelectedText();
5491 m_bMatchCase
= false;
5492 m_bLimitToDiff
= false;
5493 m_bWholeWord
= false;
5494 m_sFindText
= m_sFindText
.MakeLower();
5496 BuildFindStringArray();
5501 m_sFindText
.Empty();
5502 BuildFindStringArray();
5506 bool CBaseView::StringFound(const CString
& str
, SearchDirection srchDir
, int& start
, int& end
) const
5508 if (srchDir
== SearchPrevious
)
5511 int laststart2
= -1;
5514 laststart2
= laststart
;
5515 laststart
= str
.Find(m_sFindText
, laststart
+ 1);
5516 } while (laststart
>= 0 && laststart
< start
);
5520 start
= str
.Find(m_sFindText
, start
);
5521 end
= start
+ m_sFindText
.GetLength();
5522 bool bStringFound
= (start
>= 0);
5523 if (bStringFound
&& m_bWholeWord
)
5526 bStringFound
= IsWordSeparator(str
.Mid(start
-1,1).GetAt(0));
5530 if (str
.GetLength() > end
)
5531 bStringFound
= IsWordSeparator(str
.Mid(end
, 1).GetAt(0));
5534 return bStringFound
;
5537 void CBaseView::OnEditFindprev()
5539 Search(SearchPrevious
, false, true, false);
5542 void CBaseView::OnEditFindnext()
5544 Search(SearchNext
, false, true, false);
5547 bool CBaseView::Search(SearchDirection srchDir
, bool useStart
, bool flashIfNotFound
, bool stopEof
)
5549 if (m_sFindText
.IsEmpty())
5554 POINT start
= useStart
? m_ptSelectionViewPosStart
: m_ptSelectionViewPosEnd
;
5556 end
.y
= m_pViewData
->GetCount()-1;
5560 if (srchDir
==SearchNext
)
5561 end
.x
= GetViewLineLength(end
.y
);
5564 end
.x
= m_ptSelectionViewPosStart
.x
;
5568 if (!HasTextSelection())
5570 start
.y
= m_ptCaretViewPos
.y
;
5571 if (srchDir
==SearchNext
)
5572 start
.x
= m_ptCaretViewPos
.x
;
5576 end
.x
= m_ptCaretViewPos
.x
;
5579 CString sSelectedText
;
5581 for (int nViewLine
=start
.y
; ;srchDir
==SearchNext
? nViewLine
++ : nViewLine
--)
5587 nViewLine
= m_pViewData
->GetCount()-1;
5588 startline
= start
.y
;
5589 if (flashIfNotFound
)
5592 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED
)), RGB(63, 127, 47));
5593 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5596 if (nViewLine
> end
.y
)
5601 startline
= start
.y
;
5602 if (flashIfNotFound
)
5605 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED
)), RGB(63, 127, 47));
5606 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5609 switch (m_pViewData
->GetState(nViewLine
))
5611 case DIFFSTATE_EMPTY
:
5613 case DIFFSTATE_UNKNOWN
:
5614 case DIFFSTATE_NORMAL
:
5615 case DIFFSTATE_FILTEREDDIFF
:
5618 case DIFFSTATE_REMOVED
:
5619 case DIFFSTATE_REMOVEDWHITESPACE
:
5620 case DIFFSTATE_ADDED
:
5621 case DIFFSTATE_ADDEDWHITESPACE
:
5622 case DIFFSTATE_WHITESPACE
:
5623 case DIFFSTATE_WHITESPACE_DIFF
:
5624 case DIFFSTATE_CONFLICTED
:
5625 case DIFFSTATE_CONFLICTED_IGNORED
:
5626 case DIFFSTATE_CONFLICTADDED
:
5627 case DIFFSTATE_CONFLICTEMPTY
:
5628 case DIFFSTATE_CONFLICTRESOLVED
:
5629 case DIFFSTATE_IDENTICALREMOVED
:
5630 case DIFFSTATE_IDENTICALADDED
:
5631 case DIFFSTATE_THEIRSREMOVED
:
5632 case DIFFSTATE_THEIRSADDED
:
5633 case DIFFSTATE_YOURSREMOVED
:
5634 case DIFFSTATE_YOURSADDED
:
5635 case DIFFSTATE_EDITED
:
5637 sSelectedText
= GetViewLineChars(nViewLine
);
5638 if (nViewLine
== start
.y
&& startline
< 0)
5639 sSelectedText
= srchDir
== SearchNext
? sSelectedText
.Mid(start
.x
) : sSelectedText
.Left(end
.x
);
5641 sSelectedText
= sSelectedText
.MakeLower();
5642 int startfound
= srchDir
== SearchNext
? 0 : sSelectedText
.GetLength();
5644 if (StringFound(sSelectedText
, srchDir
, startfound
, endfound
))
5646 HighlightViewLines(nViewLine
, nViewLine
);
5647 m_ptSelectionViewPosStart
.x
= startfound
;
5648 m_ptSelectionViewPosEnd
.x
= endfound
;
5649 if (nViewLine
== start
.y
&& startline
< 0)
5651 m_ptSelectionViewPosStart
.x
+= start
.x
;
5652 m_ptSelectionViewPosEnd
.x
+= start
.x
;
5654 m_ptSelectionViewPosEnd
.x
= m_ptSelectionViewPosStart
.x
+ m_sFindText
.GetLength();
5655 m_ptSelectionViewPosStart
.y
= nViewLine
;
5656 m_ptSelectionViewPosEnd
.y
= nViewLine
;
5657 m_ptCaretViewPos
= m_ptSelectionViewPosStart
;
5658 UpdateViewsCaretPosition();
5659 EnsureCaretVisible();
5669 if (nViewLine
== startline
)
5671 if (flashIfNotFound
)
5674 message
.Format(IDS_FIND_NOTFOUND
, static_cast<LPCTSTR
>(m_sFindText
));
5676 m_pFindDialog
->SetStatusText(message
, RGB(255, 0, 0));
5677 ::MessageBeep(0xFFFFFFFF);
5678 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 3, 100);
5684 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
5688 CString
CBaseView::GetSelectedText() const
5690 CString sSelectedText
;
5691 POINT start
= m_ptSelectionViewPosStart
;
5692 POINT end
= m_ptSelectionViewPosEnd
;
5693 if (!HasTextSelection())
5695 if (!HasSelection())
5696 return sSelectedText
;
5697 start
.y
= m_nSelViewBlockStart
;
5699 end
.y
= m_nSelViewBlockEnd
;
5700 end
.x
= GetViewLineLength(m_nSelViewBlockEnd
);
5703 return sSelectedText
;
5704 // first store the selected lines in one CString
5705 for (int nViewLine
=start
.y
; nViewLine
<=end
.y
; nViewLine
++)
5707 switch (m_pViewData
->GetState(nViewLine
))
5709 case DIFFSTATE_EMPTY
:
5711 case DIFFSTATE_UNKNOWN
:
5712 case DIFFSTATE_NORMAL
:
5713 case DIFFSTATE_REMOVED
:
5714 case DIFFSTATE_REMOVEDWHITESPACE
:
5715 case DIFFSTATE_ADDED
:
5716 case DIFFSTATE_ADDEDWHITESPACE
:
5717 case DIFFSTATE_WHITESPACE
:
5718 case DIFFSTATE_WHITESPACE_DIFF
:
5719 case DIFFSTATE_CONFLICTED
:
5720 case DIFFSTATE_CONFLICTED_IGNORED
:
5721 case DIFFSTATE_CONFLICTADDED
:
5722 case DIFFSTATE_CONFLICTEMPTY
:
5723 case DIFFSTATE_CONFLICTRESOLVED
:
5724 case DIFFSTATE_IDENTICALREMOVED
:
5725 case DIFFSTATE_IDENTICALADDED
:
5726 case DIFFSTATE_THEIRSREMOVED
:
5727 case DIFFSTATE_THEIRSADDED
:
5728 case DIFFSTATE_YOURSREMOVED
:
5729 case DIFFSTATE_YOURSADDED
:
5730 case DIFFSTATE_EDITED
:
5731 case DIFFSTATE_FILTEREDDIFF
:
5732 sSelectedText
+= GetViewLineChars(nViewLine
);
5733 sSelectedText
+= L
"\r\n";
5737 // remove the non-selected chars from the first line, last line and last \r\n
5738 int nLeftCut
= start
.x
;
5739 int nRightCut
= GetViewLineChars(end
.y
).GetLength() - end
.x
+ 2;
5740 sSelectedText
= sSelectedText
.Mid(nLeftCut
, sSelectedText
.GetLength()-nLeftCut
-nRightCut
);
5741 return sSelectedText
;
5744 void CBaseView::CheckModifications(bool& hasMods
, bool& hasConflicts
, bool& hasWhitespaceMods
, bool& hasFilteredMods
)
5747 hasConflicts
= false;
5748 hasWhitespaceMods
= false;
5749 hasFilteredMods
= false;
5753 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5755 DiffStates state
= m_pViewData
->GetState(i
);
5758 case DIFFSTATE_ADDED
:
5759 case DIFFSTATE_IDENTICALADDED
:
5760 case DIFFSTATE_THEIRSADDED
:
5761 case DIFFSTATE_YOURSADDED
:
5762 case DIFFSTATE_CONFLICTADDED
:
5763 case DIFFSTATE_IDENTICALREMOVED
:
5764 case DIFFSTATE_REMOVED
:
5765 case DIFFSTATE_THEIRSREMOVED
:
5766 case DIFFSTATE_YOURSREMOVED
:
5767 case DIFFSTATE_EMPTY
:
5770 case DIFFSTATE_CONFLICTED
:
5771 case DIFFSTATE_CONFLICTED_IGNORED
:
5772 hasConflicts
= true;
5774 case DIFFSTATE_REMOVEDWHITESPACE
:
5775 case DIFFSTATE_ADDEDWHITESPACE
:
5776 case DIFFSTATE_WHITESPACE
:
5777 case DIFFSTATE_WHITESPACE_DIFF
:
5778 hasWhitespaceMods
= true;
5780 case DIFFSTATE_FILTEREDDIFF
:
5781 hasFilteredMods
= true;
5788 void CBaseView::OnEditGotoline()
5792 // find the last and first line number
5793 int nViewLineCount
= m_pViewData
->GetCount();
5795 int nLastLineNumber
= DIFF_EMPTYLINENUMBER
;
5796 for (int nViewLine
=nViewLineCount
-1; nViewLine
>=0; --nViewLine
)
5798 nLastLineNumber
= m_pViewData
->GetLineNumber(nViewLine
);
5799 if (nLastLineNumber
!=DIFF_EMPTYLINENUMBER
)
5804 if (nLastLineNumber
==DIFF_EMPTYLINENUMBER
|| nLastLineNumber
==0) // not numbered line foud or last one is first
5809 int nFirstLineNumber
=1; // first is always 1
5812 sText
.FormatMessage(IDS_GOTOLINE
, nFirstLineNumber
, nLastLineNumber
);
5814 CGotoLineDlg
dlg(this);
5815 dlg
.SetLabel(sText
);
5816 dlg
.SetLimits(nFirstLineNumber
, nLastLineNumber
);
5817 if (dlg
.DoModal() == IDOK
)
5819 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; ++nViewLine
)
5821 if ((m_pViewData
->GetLineNumber(nViewLine
)+1) == dlg
.GetLineNumber())
5823 HighlightViewLines(nViewLine
, nViewLine
);
5830 int CBaseView::SaveFile(int nFlags
)
5833 if (m_pViewData
&& m_pWorkingFile
)
5835 CFileTextLines file
;
5836 m_SaveParams
.m_LineEndings
= m_lineendings
;
5837 m_SaveParams
.m_UnicodeType
= m_texttype
;
5838 file
.SetSaveParams(m_SaveParams
);
5840 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5842 //only copy non-removed lines
5843 DiffStates state
= m_pViewData
->GetState(i
);
5846 case DIFFSTATE_CONFLICTED
:
5847 case DIFFSTATE_CONFLICTED_IGNORED
:
5854 } while((last
<m_pViewData
->GetCount()) && ((m_pViewData
->GetState(last
)==DIFFSTATE_CONFLICTED
)||(m_pViewData
->GetState(last
)==DIFFSTATE_CONFLICTED_IGNORED
)));
5855 file
.Add(L
"<<<<<<< .mine", EOL_NOENDING
);
5856 for (int j
=first
; j
<last
; j
++)
5858 file
.Add(m_pwndRight
->m_pViewData
->GetLine(j
), m_pwndRight
->m_pViewData
->GetLineEnding(j
));
5860 file
.Add(L
"=======", EOL_NOENDING
);
5861 for (int j
=first
; j
<last
; j
++)
5863 file
.Add(m_pwndLeft
->m_pViewData
->GetLine(j
), m_pwndLeft
->m_pViewData
->GetLineEnding(j
));
5865 file
.Add(L
">>>>>>> .theirs", EOL_NOENDING
);
5869 case DIFFSTATE_EMPTY
:
5871 case DIFFSTATE_CONFLICTEMPTY
:
5872 case DIFFSTATE_IDENTICALREMOVED
:
5873 case DIFFSTATE_REMOVED
:
5874 case DIFFSTATE_THEIRSREMOVED
:
5875 case DIFFSTATE_YOURSREMOVED
:
5876 case DIFFSTATE_CONFLICTRESOLVEDEMPTY
:
5877 if ((nFlags
&SAVE_REMOVEDLINES
) == 0)
5879 // do not save removed lines
5883 file
.Add(m_pViewData
->GetLine(i
), m_pViewData
->GetLineEnding(i
));
5887 CString filename
= m_pWorkingFile
->GetFilename();
5888 if (m_pWorkingFile
->IsReadonly())
5889 if (!CCommonAppUtils::FileOpenSave(filename
, nullptr, IDS_SAVEASTITLE
, IDS_COMMONFILEFILTER
, false, m_hWnd
))
5891 if (!file
.Save(filename
))
5893 ::MessageBox(m_hWnd
, file
.GetErrorString(), L
"TortoiseGitMerge", MB_ICONERROR
);
5896 m_pWorkingFile
->SetFileName(filename
);
5897 m_pWorkingFile
->StoreFileAttributes();
5898 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5900 CUndo::GetInstance().MarkAsOriginalState(
5902 this == m_pwndRight
,
5903 this == m_pwndBottom
);
5904 if (file
.GetCount() == 1 && file
.GetAt(0).IsEmpty() && file
.GetLineEnding(0) == EOL_NOENDING
)
5906 return file
.GetCount();
5912 int CBaseView::SaveFileTo(CString sFileName
, int nFlags
)
5916 m_pWorkingFile
->SetFileName(sFileName
);
5917 return SaveFile(nFlags
);
5923 EOL
CBaseView::GetLineEndings()
5925 return GetLineEndings(GetWhitecharsProperties().HasMixedEols
);
5928 EOL
CBaseView::GetLineEndings(bool bHasMixedEols
)
5932 return EOL_AUTOLINE
; // mixed eols - hack value
5934 if (m_lineendings
== EOL_AUTOLINE
)
5938 return m_lineendings
;
5941 void CBaseView::ReplaceLineEndings(EOL eEol
)
5943 if (eEol
== EOL_AUTOLINE
)
5948 m_lineendings
= eEol
;
5949 // replace all set EOLs
5950 // TODO store line endings and lineendings in undo
5951 //CUndo::BeginGrouping();
5952 for (int i
= 0; i
< GetViewCount(); ++i
)
5958 EOL eLineEol
= GetViewLineEnding(i
);
5959 if (eLineEol
== EOL_AUTOLINE
|| eLineEol
== EOL_NOENDING
|| eLineEol
== m_lineendings
)
5963 SetViewLineEnding(i
, eEol
);
5965 //CUndo::EndGrouping();
5966 //CUndo::saveundostep;
5971 void CBaseView::SetLineEndingStyle(EOL eEol
)
5973 m_lineendings
= eEol
;
5976 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType
)
5978 if (m_texttype
== eTextType
)
5982 m_texttype
= eTextType
;
5987 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId
)
5990 return; // nothing to be changed in read-only view
5992 dlg
.view
.LoadString(nTextId
);
5993 dlg
.texttype
= m_texttype
;
5994 dlg
.lineendings
= GetLineEndings();
5995 if (dlg
.DoModal() != IDOK
)
5997 SetTextType(dlg
.texttype
);
5998 ReplaceLineEndings(dlg
.lineendings
);
6002 Replaces lines from source view to this
6004 void CBaseView::UseViewBlock(CBaseView
* pwndView
, int nFirstViewLine
, int nLastViewLine
, std::function
<bool(int)> fnSkip
)
6006 if (!IsViewGood(pwndView
))
6010 CUndo::GetInstance().BeginGrouping();
6012 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6014 bool skip
= fnSkip(viewLine
);
6017 if (GetViewMarked(viewLine
))
6018 SetViewMarked(viewLine
, false);
6021 viewdata line
= pwndView
->GetViewData(viewLine
);
6022 if (line
.ending
!= EOL_NOENDING
)
6023 line
.ending
= m_lineendings
;
6026 case DIFFSTATE_CONFLICTEMPTY
:
6027 case DIFFSTATE_UNKNOWN
:
6028 line
.state
= DIFFSTATE_EMPTY
;
6029 case DIFFSTATE_EMPTY
:
6031 case DIFFSTATE_ADDED
:
6032 case DIFFSTATE_CONFLICTADDED
:
6033 case DIFFSTATE_CONFLICTED
:
6034 case DIFFSTATE_CONFLICTED_IGNORED
:
6035 case DIFFSTATE_IDENTICALADDED
:
6036 case DIFFSTATE_THEIRSADDED
:
6037 case DIFFSTATE_YOURSADDED
:
6038 case DIFFSTATE_IDENTICALREMOVED
:
6039 case DIFFSTATE_REMOVED
:
6040 case DIFFSTATE_THEIRSREMOVED
:
6041 case DIFFSTATE_YOURSREMOVED
:
6042 pwndView
->SetViewState(viewLine
, DIFFSTATE_NORMAL
);
6043 line
.state
= DIFFSTATE_NORMAL
;
6044 case DIFFSTATE_NORMAL
:
6049 bool marked
= GetViewMarked(viewLine
);
6050 SetViewData(viewLine
, line
);
6052 SetViewMarked(viewLine
, false);
6053 if ((m_texttype
== UnicodeType::ASCII
) && (pwndView
->GetTextType() != UnicodeType::ASCII
))
6055 // if this view is in ASCII and the other is not, we have to make sure that
6056 // the text we copy from the other view can actually be saved in ASCII encoding.
6057 // if not, we have to change this views encoding to the same encoding as the other view
6058 BOOL useDefault
= FALSE
;
6059 WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, line
.sLine
, -1, nullptr, 0, 0, &useDefault
);
6060 if (useDefault
) // a default char is required, so the char can not be saved as ASCII
6061 SetTextType(pwndView
->GetTextType());
6064 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6065 // TODO: check if copied line is same as original one set modified only when differ
6069 int nRemovedLines
= CleanEmptyLines();
6072 // make sure all non empty line have EOL set but last
6073 // wrong can be last copied line(have eol, but no line under),
6074 // or old last line (line before copied block missing eol, but have line under)
6075 // we'll check all lines to be sure
6076 int nLine
= GetViewCount();
6077 // check last line have no EOL set
6080 if (!IsViewLineEmpty(nLine
))
6082 if (GetViewLineEnding(nLine
) != EOL_NOENDING
)
6084 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6085 // so next line is empty
6086 ASSERT(IsViewLineEmpty(nLine
+1));
6087 // and we can turn it to normal empty line
6088 SetViewData(nLine
+1, viewdata(CString(), DIFFSTATE_ADDED
, 1, EOL_NOENDING
, HIDESTATE_SHOWN
));
6093 // check all (nonlast) line have EOL set
6096 if (!IsViewLineEmpty(nLine
))
6098 if (GetViewLineEnding(nLine
) == EOL_NOENDING
)
6100 SetViewLineEnding(nLine
, m_lineendings
);
6101 // in theory there should be only one line needing fix, but most of time we get over all anyway
6107 UpdateViewLineNumbers();
6110 CUndo::GetInstance().EndGrouping();
6112 if (nRemovedLines
!=0)
6114 // some lines are gone update selection
6116 SetupAllViewSelection(nFirstViewLine
, nLastViewLine
- nRemovedLines
);
6118 BuildAllScreen2ViewVector();
6119 pwndView
->Invalidate();
6123 void CBaseView::MarkBlock(bool marked
, int nFirstViewLine
, int nLastViewLine
)
6127 CUndo::GetInstance().BeginGrouping();
6129 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6130 SetViewMarked(viewLine
, marked
);
6134 CUndo::GetInstance().EndGrouping();
6136 BuildAllScreen2ViewVector();
6141 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView
*pwndView
)
6143 auto fn
= [this](int viewLine
) -> bool { return GetViewMarked(viewLine
) || GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6144 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6147 void CBaseView::UseViewFileOfMarked(CBaseView
*pwndView
)
6149 auto fn
= [this](int viewLine
) -> bool { return !GetViewMarked(viewLine
) || GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6150 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6153 void CBaseView::UseViewFileExceptEdited(CBaseView
*pwndView
)
6155 auto fn
= [this](int viewLine
) -> bool { return GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6156 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6159 int CBaseView::GetIndentCharsForLine(int x
, int y
)
6161 const int maxGuessLine
= 100;
6163 const CString
& line
= GetViewLine(y
);
6164 if (m_nTabMode
& TABMODE_SMARTINDENT
)
6166 // if the line contains one tab, use tabs
6167 // we can not test for spaces, since even if tabs are used,
6168 // spaces are used in a tabified file for alignment.
6169 if (line
.Find(L
'\t') >= 0)
6170 nTabMode
= 0; // use tabs
6171 else if (line
.GetLength() > m_nTabSize
)
6172 nTabMode
= 1; // use spaces
6174 if (m_nTabMode
& TABMODE_SMARTINDENT
)
6176 // detect lines nearby
6177 for (int i
= y
- 1, j
= y
+ 1; nTabMode
== -1; --i
, ++j
)
6179 bool above
= i
> 0 && i
>= y
- maxGuessLine
;
6180 bool below
= j
< GetViewCount() && j
<= y
+ maxGuessLine
;
6181 if (!(above
|| below
))
6183 auto ac
= GetViewLine(i
);
6184 auto bc
= GetViewLine(j
);
6185 if ((ac
.Find(L
'\t') >= 0) || (bc
.Find(L
'\t') >= 0))
6190 else if ((ac
.GetLength() > m_nTabSize
) && (bc
.GetLength() > m_nTabSize
))
6199 nTabMode
= m_nTabMode
& TABMODE_USESPACES
;
6204 x
= CountExpandedChars(line
, x
);
6205 return (m_nTabSize
- (x
% m_nTabSize
));
6212 void CBaseView::AddIndentationForSelectedBlock()
6214 bool bModified
= false;
6215 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6217 // skip the line if no character is selected in the last selected line
6218 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6223 if (IsLineEmpty(nViewLine
))
6227 const CString
&sLine
= GetViewLine(nViewLine
);
6228 CString sTemp
= sLine
;
6229 if (sTemp
.Trim().IsEmpty())
6231 // skip empty and whitechar only lines
6234 // add tab to line start (alternatively m_nTabSize spaces can be used)
6236 int indentChars
= GetIndentCharsForLine(0, nViewLine
);
6237 tabStr
= indentChars
> 0 ? CString(L
' ', indentChars
) : L
"\t";
6238 SetViewLine(nViewLine
, tabStr
+ sLine
);
6245 BuildAllScreen2ViewVector();
6249 void CBaseView::RemoveIndentationForSelectedBlock()
6251 bool bModified
= false;
6252 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6254 // skip the line if no character is selected in the last selected line
6255 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6260 if (IsLineEmpty(nViewLine
))
6264 CString sLine
= GetViewLine(nViewLine
);
6265 // remove up to n spaces from line start
6266 // and one tab (if less then n spaces was removed)
6268 while (nPos
<m_nTabSize
)
6270 switch (sLine
[nPos
])
6282 sLine
.Delete(0, nPos
);
6283 SetViewLine(nViewLine
, sLine
);
6291 BuildAllScreen2ViewVector();
6296 there are two possible versions
6297 - convert tabs to spaces only in front of text (implemented)
6298 - convert all tabs to spaces
6300 void CBaseView::ConvertTabToSpaces()
6302 bool bModified
= false;
6303 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6305 if (IsLineEmpty(nViewLine
))
6309 const CString
&sLine
= GetViewLine(nViewLine
);
6310 bool bTabToConvertFound
= false;
6313 while (nPosIn
<sLine
.GetLength())
6315 switch (sLine
[nPosIn
])
6323 bTabToConvertFound
= true;
6324 nPosOut
= (nPosOut
+m_nTabSize
) - nPosOut
%m_nTabSize
;
6329 if (bTabToConvertFound
)
6331 CString sLineNew
= sLine
;
6332 sLineNew
.Delete(0, nPosIn
);
6333 sLineNew
= CString(' ', nPosOut
) + sLineNew
;
6334 SetViewLine(nViewLine
, sLineNew
);
6342 BuildAllScreen2ViewVector();
6347 there are two possible version
6348 - convert spaces to tabs only in front of text (implemented)
6349 - convert all spaces to tabs
6351 void CBaseView::Tabularize()
6353 bool bModified
= false;
6354 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6356 if (IsLineEmpty(nViewLine
))
6360 const CString
&sLine
= GetViewLine(nViewLine
);
6362 int nTabCount
= 0; // total tabs to be used
6363 int nSpaceCount
= 0; // number of spaces in tab size run
6365 while (nPos
<sLine
.GetLength())
6367 switch (sLine
[nPos
++])
6371 if (++nSpaceCount
< m_nTabSize
)
6385 CString sLineNew
= sLine
;
6386 sLineNew
.Delete(0, nDel
);
6387 sLineNew
= CString('\t', nTabCount
) + sLineNew
;
6388 if (sLine
!=sLineNew
)
6390 SetViewLine(nViewLine
, sLineNew
);
6399 BuildAllScreen2ViewVector();
6403 void CBaseView::RemoveTrailWhiteChars()
6405 bool bModified
= false;
6406 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6408 if (IsLineEmpty(nViewLine
))
6412 const CString
&sLine
= GetViewLine(nViewLine
);
6413 CString sLineNew
= sLine
;
6414 sLineNew
.TrimRight();
6415 if (sLine
.GetLength()!=sLineNew
.GetLength())
6417 SetViewLine(nViewLine
, sLineNew
);
6425 BuildAllScreen2ViewVector();
6429 CBaseView::TWhitecharsProperties
CBaseView::GetWhitecharsProperties()
6431 if (GetViewCount()>10000)
6433 // 10k lines is enough to check
6434 TWhitecharsProperties oRet
= {true, true, true, true};
6437 TWhitecharsProperties oRet
= {};
6438 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6440 if (IsLineEmpty(nViewLine
))
6444 const CString
&sLine
= GetViewLine(nViewLine
);
6445 if (sLine
.IsEmpty())
6449 // check leading whites for convertible tabs and spaces
6451 int nSpaceCount
= 0; // number of spaces in tab size run
6452 while (nPos
<sLine
.GetLength() && (!oRet
.HasSpacesToConvert
|| !oRet
.HasTabsToConvert
))
6454 switch (sLine
[nPos
++])
6457 if (++nSpaceCount
>= m_nTabSize
)
6459 oRet
.HasSpacesToConvert
= true;
6463 oRet
.HasTabsToConvert
= true;
6466 oRet
.HasSpacesToConvert
= true;
6473 // check trailing whites for removable chars
6474 switch (sLine
[sLine
.GetLength()-1])
6478 oRet
.HasTrailWhiteChars
= true;
6482 EOL eLineEol
= GetViewLineEnding(nViewLine
);
6483 if (!oRet
.HasMixedEols
&& (eLineEol
!= m_lineendings
) && (eLineEol
!= EOL_AUTOLINE
) && (eLineEol
!= EOL_NOENDING
))
6485 oRet
.HasMixedEols
= true;
6491 void CBaseView::InsertText(const CString
& sText
)
6495 POINT ptCaretViewPos
= GetCaretViewPosition();
6496 int nLeft
= ptCaretViewPos
.x
;
6497 int nViewLine
= ptCaretViewPos
.y
;
6499 if ((nViewLine
== 0) && (GetViewCount() == 0))
6500 OnChar(VK_RETURN
, 0, 0);
6502 std::vector
<CString
> lines
;
6505 while ((nEolPos
= sText
.Find('\r', nEolPos
)) >= 0)
6507 CString sLine
= sText
.Mid(nStart
, nEolPos
- nStart
);
6508 lines
.push_back(sLine
);
6512 CString sLine
= sText
.Mid(nStart
);
6513 lines
.push_back(sLine
);
6515 int nLinesToPaste
= static_cast<int>(lines
.size());
6516 if (nLinesToPaste
> 1)
6520 // We want to undo the multiline insertion in a single step.
6521 CUndo::GetInstance().BeginGrouping();
6523 sLine
= GetViewLineChars(nViewLine
);
6524 CString sLineLeft
= sLine
.Left(nLeft
);
6525 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
6526 EOL eOriginalEnding
= GetViewLineEnding(nViewLine
);
6527 viewdata
newLine(L
"", DIFFSTATE_EDITED
, 1, m_lineendings
, HIDESTATE_SHOWN
);
6528 if (!lines
[0].IsEmpty() || !sLineRight
.IsEmpty() || (eOriginalEnding
!= m_lineendings
))
6530 newLine
.sLine
= sLineLeft
+ lines
[0];
6531 SetViewData(nViewLine
, newLine
);
6534 int nInsertLine
= nViewLine
;
6535 for (int i
= 1; i
< nLinesToPaste
- 1; i
++)
6537 newLine
.sLine
= lines
[i
];
6538 InsertViewData(++nInsertLine
, newLine
);
6540 newLine
.sLine
= lines
[nLinesToPaste
- 1] + sLineRight
;
6541 newLine
.ending
= eOriginalEnding
;
6542 InsertViewData(++nInsertLine
, newLine
);
6547 // adds new lines everywhere except me
6548 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!= this)
6550 m_pwndLeft
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6552 if (IsViewGood(m_pwndRight
) && m_pwndRight
!= this)
6554 m_pwndRight
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6556 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!= this)
6558 m_pwndBottom
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6562 UpdateViewLineNumbers();
6563 CUndo::GetInstance().EndGrouping();
6565 ptCaretViewPos
= SetupPoint(lines
[nLinesToPaste
- 1].GetLength(), nInsertLine
);
6569 // single line text - just insert it
6570 sLine
= GetViewLineChars(nViewLine
);
6571 sLine
.Insert(nLeft
, sText
);
6572 ptCaretViewPos
= SetupPoint(nLeft
+ sText
.GetLength(), nViewLine
);
6573 SetViewLine(nViewLine
, sLine
);
6575 auto viewState
= GetViewState(nViewLine
);
6576 if (IsStateEmpty(viewState
) || IsStateConflicted(viewState
) || viewState
== DIFFSTATE_IDENTICALREMOVED
)
6578 // if not last line set EOL
6579 for (int nCheckViewLine
= nViewLine
+ 1; nCheckViewLine
< GetViewCount(); ++nCheckViewLine
)
6581 if (!IsViewLineEmpty(nCheckViewLine
))
6583 SetViewLineEnding(nViewLine
, m_lineendings
);
6587 // make sure previous (non empty) line have EOL set
6588 for (int nCheckViewLine
= nViewLine
- 1; nCheckViewLine
> 0; --nCheckViewLine
)
6590 if (!IsViewLineEmpty(nCheckViewLine
))
6592 if (GetViewLineEnding(nCheckViewLine
) == EOL_NOENDING
)
6593 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
6599 SetViewState(nViewLine
, DIFFSTATE_EDITED
);
6605 BuildAllScreen2ViewVector();
6606 UpdateCaretViewPosition(ptCaretViewPos
);
6609 ULONG
CBaseView::GetGestureStatus(CPoint
/*ptTouch*/)