1
// TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2021 - TortoiseSVN
4 // Copyright (C) 2011-2012, 2017-2021 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"
34 #include "DarkModeHelper.h"
35 #include "ClipboardHelper.h"
38 // We use three different kind of lines here:
39 // 1. The real lines of the original files.
40 // These are shown in the view margin and are not used elsewhere, they're only for user information.
42 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
44 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
46 // Basically view lines are the line data, while screen lines are shown lines.
53 #define HEADERHEIGHT (CDPIAware::Instance().ScaleY(10))
55 #define IDT_SCROLLTIMER 101
57 CBaseView
* CBaseView::m_pwndLeft
= nullptr;
58 CBaseView
* CBaseView::m_pwndRight
= nullptr;
59 CBaseView
* CBaseView::m_pwndBottom
= nullptr;
60 CLocatorBar
* CBaseView::m_pwndLocator
= nullptr;
61 CLineDiffBar
* CBaseView::m_pwndLineDiffBar
= nullptr;
62 CMFCStatusBar
* CBaseView::m_pwndStatusBar
= nullptr;
63 CMFCRibbonStatusBar
* CBaseView::m_pwndRibbonStatusBar
= nullptr;
64 CMainFrame
* CBaseView::m_pMainFrame
= nullptr;
65 CBaseView::Screen2View
CBaseView::m_Screen2View
;
66 const UINT
CBaseView::m_FindDialogMessage
= RegisterWindowMessage(FINDMSGSTRING
);
68 allviewstate
CBaseView::m_AllState
;
70 IMPLEMENT_DYNCREATE(CBaseView
, CView
)
72 CBaseView::CBaseView()
73 : m_pCacheBitmap(nullptr)
74 , m_pViewData(nullptr)
75 , m_pOtherViewData(nullptr)
76 , m_pOtherView(nullptr)
80 , m_nLastScreenChars(-1)
81 , m_nMaxLineLength(-1)
88 , m_mouseInMargin(false)
90 , m_lineendings(EOL_AUTOLINE
)
92 , m_bReadonlyIsChangable(false)
95 , m_nSelViewBlockStart(-1)
96 , m_nSelViewBlockEnd(-1)
98 , m_bShowSelection(true)
99 , m_texttype(CFileTextLines::AUTOTYPE
)
101 , m_bOtherDiffChecked(false)
102 , m_bInlineWordDiff(true)
103 , m_bWhitespaceInlineDiffs(false)
105 , m_pFindDialog(nullptr)
107 , m_bMatchCase(false)
108 , m_bLimitToDiff(true)
109 , m_bWholeWord(false)
111 , m_pWorkingFile(nullptr)
112 , m_bInsertMode(true)
113 , m_bEditorConfigEnabled(false)
114 , m_bEditorConfigLoaded(2) // 2 = not evaluated
116 , m_themeCallbackId(0)
117 , m_MarkedWordCount(0)
119 m_ptCaretViewPos
.x
= 0;
120 m_ptCaretViewPos
.y
= 0;
121 m_ptSelectionViewPosStart
= m_ptCaretViewPos
;
122 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosStart
;
123 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosEnd
;
124 m_bViewWhitespace
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
125 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
126 m_bShowInlineDiff
= CRegDWORD(L
"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE
);
127 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
128 m_InlineAddedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\InlineAdded", INLINEADDED_COLOR
);
129 m_InlineRemovedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\InlineRemoved", INLINEREMOVED_COLOR
);
130 m_ModifiedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR
);
131 m_InlineAddedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkInlineAdded", INLINEADDED_DARK_COLOR
);
132 m_InlineRemovedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkInlineRemoved", INLINEREMOVED_DARK_COLOR
);
133 m_ModifiedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkColorModifiedB", MODIFIED_DARK_COLOR
);
134 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\Whitespace", CTheme::Instance().GetThemeColor(GetSysColor(COLOR_3DSHADOW
)));
135 m_sWordSeparators
= CRegString(L
"Software\\TortoiseGitMerge\\WordSeparators", L
"[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?");
136 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
137 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
138 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
139 m_bEditorConfigEnabled
= !!static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE
));
140 std::fill_n(m_apFonts
, fontsCount
, static_cast<CFont
*>(nullptr));
142 int cxIcon
= GetSystemMetrics(SM_CXSMICON
);
143 int cyIcon
= GetSystemMetrics(SM_CYSMICON
);
144 m_hConflictedIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDLINE
, cxIcon
, cyIcon
);
145 m_hConflictedIgnoredIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDIGNOREDLINE
, cxIcon
, cyIcon
);
146 m_hRemovedIcon
= CCommonAppUtils::LoadIconEx(IDI_REMOVEDLINE
, cxIcon
, cyIcon
);
147 m_hAddedIcon
= CCommonAppUtils::LoadIconEx(IDI_ADDEDLINE
, cxIcon
, cyIcon
);
148 m_hWhitespaceBlockIcon
= CCommonAppUtils::LoadIconEx(IDI_WHITESPACELINE
, cxIcon
, cyIcon
);
149 m_hEqualIcon
= CCommonAppUtils::LoadIconEx(IDI_EQUALLINE
, cxIcon
, cyIcon
);
150 m_hLineEndingCR
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCR
, cxIcon
, cyIcon
);
151 m_hLineEndingCRLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCRLF
, cxIcon
, cyIcon
);
152 m_hLineEndingLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGLF
, cxIcon
, cyIcon
);
153 m_hEditedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEEDITED
, cxIcon
, cyIcon
);
154 m_hMovedIcon
= CCommonAppUtils::LoadIconEx(IDI_MOVEDLINE
, cxIcon
, cyIcon
);
155 m_hMarkedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEMARKED
, cxIcon
, cyIcon
);
156 m_margincursor
= static_cast<HCURSOR
>(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR
), IMAGE_CURSOR
, 0, 0, LR_DEFAULTSIZE
));
158 for (int i
=0; i
<1024; ++i
)
159 m_sConflictedText
+= L
"??";
160 m_sNoLineNr
.LoadString(IDS_EMPTYLINETT
);
164 SecureZeroMemory(&m_lfBaseFont
, sizeof(m_lfBaseFont
));
167 m_Eols
[EOL_LF
] = L
"\n"; // x0a
168 m_Eols
[EOL_CR
] = L
"\r"; // x0d
169 m_Eols
[EOL_CRLF
] = L
"\r\n"; // x0d x0a
170 m_Eols
[EOL_LFCR
] = L
"\n\r";
171 m_Eols
[EOL_VT
] = L
"\v"; // x0b
172 m_Eols
[EOL_FF
] = L
"\f"; // x0c
173 m_Eols
[EOL_NEL
] = L
"\x85";
174 m_Eols
[EOL_LS
] = L
"\x2028";
175 m_Eols
[EOL_PS
] = L
"\x2029";
176 m_Eols
[EOL_AUTOLINE
] = m_Eols
[m_lineendings
==EOL_AUTOLINE
179 m_SaveParams
.m_LineEndings
= EOL::EOL_AUTOLINE
;
180 m_SaveParams
.m_UnicodeType
= CFileTextLines::AUTOTYPE
;
182 m_themeCallbackId
= CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme()); });
185 CBaseView::~CBaseView()
187 CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId
);
190 DestroyCursor(m_margincursor
);
193 BEGIN_MESSAGE_MAP(CBaseView
, CView
)
206 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE
, OnMergeNextdifference
)
207 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE
, OnMergePreviousdifference
)
208 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
209 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
212 ON_COMMAND(ID_EDIT_COPY
, OnEditCopy
)
214 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT
, OnMergePreviousconflict
)
215 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT
, OnMergeNextconflict
)
217 ON_COMMAND(ID_CARET_DOWN
, &CBaseView::OnCaretDown
)
218 ON_COMMAND(ID_CARET_LEFT
, &CBaseView::OnCaretLeft
)
219 ON_COMMAND(ID_CARET_RIGHT
, &CBaseView::OnCaretRight
)
220 ON_COMMAND(ID_CARET_UP
, &CBaseView::OnCaretUp
)
221 ON_COMMAND(ID_CARET_WORDLEFT
, &CBaseView::OnCaretWordleft
)
222 ON_COMMAND(ID_CARET_WORDRIGHT
, &CBaseView::OnCaretWordright
)
223 ON_COMMAND(ID_EDIT_CUT
, &CBaseView::OnEditCut
)
224 ON_COMMAND(ID_EDIT_PASTE
, &CBaseView::OnEditPaste
)
226 ON_WM_LBUTTONDBLCLK()
227 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF
, &CBaseView::OnNavigateNextinlinediff
)
228 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF
, &CBaseView::OnNavigatePrevinlinediff
)
229 ON_COMMAND(ID_EDIT_SELECTALL
, &CBaseView::OnEditSelectall
)
230 ON_COMMAND(ID_EDIT_FIND
, OnEditFind
)
231 ON_REGISTERED_MESSAGE(m_FindDialogMessage
, OnFindDialogMessage
)
232 ON_COMMAND(ID_EDIT_FINDNEXT
, OnEditFindnext
)
233 ON_COMMAND(ID_EDIT_FINDPREV
, OnEditFindprev
)
234 ON_COMMAND(ID_EDIT_FINDNEXTSTART
, OnEditFindnextStart
)
235 ON_COMMAND(ID_EDIT_FINDPREVSTART
, OnEditFindprevStart
)
236 ON_COMMAND(ID_EDIT_GOTOLINE
, &CBaseView::OnEditGotoline
)
241 void CBaseView::DocumentUpdated()
247 m_nLastScreenChars
= -1;
248 m_nMaxLineLength
= -1;
252 m_bOtherDiffChecked
= false;
256 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
257 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
258 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
259 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
260 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
261 m_Eols
[EOL_AUTOLINE
] = m_Eols
[m_lineendings
==EOL_AUTOLINE
264 SetEditorConfigEnabled(m_bEditorConfigEnabled
);
266 ClearCurrentSelection();
268 SetTheme(CTheme::Instance().IsDarkTheme());
272 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled
)
274 m_bEditorConfigEnabled
= bEditorConfigEnabled
;
275 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
276 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
277 if (m_bEditorConfigEnabled
)
279 m_bEditorConfigLoaded
= FALSE
; // no editorconfig entries loaded
280 CEditorConfigWrapper ec
;
281 if (ec
.Load(m_sReflectedName
.IsEmpty() ? m_sFullFilePath
: m_sReflectedName
))
283 m_bEditorConfigLoaded
= TRUE
;
284 if (ec
.m_nTabWidth
!= nullptr)
285 m_nTabSize
= ec
.m_nTabWidth
;
286 if (ec
.m_bIndentStyle
!= nullptr)
287 m_nTabMode
= (m_nTabMode
& ~TABMODE_USESPACES
) | (ec
.m_bIndentStyle
? TABMODE_USESPACES
: TABMODE_NONE
);
292 static CString
GetTabModeString(int nTabMode
, int nTabSize
, bool bEditorConfig
)
295 if (nTabMode
& TABMODE_USESPACES
)
299 text
.AppendFormat(L
" %d", nTabSize
);
300 if (nTabMode
& TABMODE_SMARTINDENT
)
307 void CBaseView::UpdateStatusBar()
309 int nRemovedLines
= 0;
311 int nConflictedLines
= 0;
315 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
317 DiffStates state
= m_pViewData
->GetState(i
);
320 case DIFFSTATE_ADDED
:
321 case DIFFSTATE_IDENTICALADDED
:
322 case DIFFSTATE_THEIRSADDED
:
323 case DIFFSTATE_YOURSADDED
:
324 case DIFFSTATE_CONFLICTADDED
:
327 case DIFFSTATE_IDENTICALREMOVED
:
328 case DIFFSTATE_REMOVED
:
329 case DIFFSTATE_THEIRSREMOVED
:
330 case DIFFSTATE_YOURSREMOVED
:
333 case DIFFSTATE_CONFLICTED
:
334 case DIFFSTATE_CONFLICTED_IGNORED
:
346 sBarText
+= CFileTextLines::GetEncodingName(m_texttype
);
347 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
348 sBarText
+= GetEolName(m_lineendings
);
349 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
351 if (sBarText
.IsEmpty())
357 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
358 if (!sBarText
.IsEmpty())
364 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
365 if (!sBarText
.IsEmpty())
369 if (nConflictedLines
)
371 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
372 if (!sBarText
.IsEmpty())
376 if (m_pwndStatusBar
|| m_pwndRibbonStatusBar
)
383 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
385 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
387 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
389 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
390 sBarText
= sTemp
+sBarText
;
392 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
394 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
395 sBarText
= sTemp
+sBarText
;
397 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
398 m_pwndStatusBar
->GetPaneInfo(nIndex
, nID
, nStyle
, cxWidth
);
399 //calculate the width of the text
400 CDC
* pDC
= m_pwndStatusBar
->GetDC();
403 CSize size
= pDC
->GetTextExtent(sBarText
);
404 m_pwndStatusBar
->SetPaneInfo(nIndex
, nID
, nStyle
, size
.cx
+2);
407 m_pwndStatusBar
->SetPaneText(nIndex
, sBarText
);
409 else if (m_pwndRibbonStatusBar
)
411 if (!IsViewGood(m_pwndBottom
))
412 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
413 if ((m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
) && (IsViewGood(this)))
415 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
416 auto apBtnGroupBottom
= std::make_unique
<CMFCRibbonButtonsGroup
>();
417 apBtnGroupBottom
->SetID(ID_INDICATOR_BOTTOMVIEW
);
418 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR
, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW
)), TRUE
));
419 CMFCRibbonButton
* pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING
, L
"");
420 m_pMainFrame
->FillEncodingButton(pButton
, ID_INDICATOR_BOTTOMENCODINGSTART
);
421 apBtnGroupBottom
->AddButton(pButton
);
422 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL
, L
"");
423 m_pMainFrame
->FillEOLButton(pButton
, ID_INDICATOR_BOTTOMEOLSTART
);
424 apBtnGroupBottom
->AddButton(pButton
);
425 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE
, L
"");
426 m_pMainFrame
->FillTabModeButton(pButton
, ID_INDICATOR_BOTTOMTABMODESTART
);
427 apBtnGroupBottom
->AddButton(pButton
);
428 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW
, L
"", TRUE
));
429 m_pwndRibbonStatusBar
->AddExtendedElement(apBtnGroupBottom
.release(), L
"");
432 CMFCRibbonButtonsGroup
* pGroup
= DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup
, m_pwndRibbonStatusBar
->FindByID(m_nStatusBarID
));
435 CMFCRibbonStatusBarPane
* pPane
= DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane
, pGroup
->GetButton(4));
438 pPane
->SetText(sBarText
);
440 CMFCRibbonButton
* pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(1));
443 pButton
->SetText(CFileTextLines::GetEncodingName(m_texttype
));
444 pButton
->SetDescription(CFileTextLines::GetEncodingName(m_texttype
));
446 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(2));
449 pButton
->SetText(GetEolName(m_lineendings
));
450 pButton
->SetDescription(GetEolName(m_lineendings
));
452 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(3));
455 pButton
->SetText(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
456 pButton
->SetDescription(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
459 m_pwndRibbonStatusBar
->RecalcLayout();
460 m_pwndRibbonStatusBar
->Invalidate();
465 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
467 if (!CView::PreCreateWindow(cs
))
470 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
471 cs
.style
&= ~WS_BORDER
;
472 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
473 ::LoadCursor(nullptr, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+ 1), nullptr);
475 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
476 if (!pParentWnd
|| !pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
478 // View must always create its own scrollbars,
479 // if only it's not used within splitter
480 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
482 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
486 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
493 if (!m_apFonts
[nIndex
])
495 m_apFonts
[nIndex
] = new CFont
;
496 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
497 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
498 m_lfBaseFont
.lfItalic
= static_cast<BYTE
>(bItalic
);
499 m_lfBaseFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\LogFontSize", 10)));
500 wcsncpy_s(m_lfBaseFont
.lfFaceName
, static_cast<LPCWSTR
>(static_cast<CString
>(CRegString(L
"Software\\TortoiseGitMerge\\LogFontName", L
"Consolas"))), _countof(m_lfBaseFont
.lfFaceName
) - 1);
501 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
503 delete m_apFonts
[nIndex
];
504 m_apFonts
[nIndex
] = nullptr;
505 return CView::GetFont();
508 return m_apFonts
[nIndex
];
511 void CBaseView::CalcLineCharDim()
516 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
517 const CSize szCharExt
= pDC
->GetTextExtent(L
"X");
518 pDC
->SelectObject(pOldFont
);
521 m_nLineHeight
= szCharExt
.cy
;
522 if (m_nLineHeight
<= 0)
524 m_nCharWidth
= szCharExt
.cx
;
525 if (m_nCharWidth
<= 0)
529 int CBaseView::GetScreenChars()
531 if (m_nScreenChars
== -1)
534 GetClientRect(&rect
);
535 m_nScreenChars
= (rect
.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL
)) / GetCharWidth();
536 if (m_nScreenChars
< 0)
539 return m_nScreenChars
;
542 int CBaseView::GetAllMinScreenChars() const
544 int nChars
= INT_MAX
;
545 if (IsLeftViewGood())
546 nChars
= std::min
<int>(nChars
, m_pwndLeft
->GetScreenChars());
547 if (IsRightViewGood())
548 nChars
= std::min
<int>(nChars
, m_pwndRight
->GetScreenChars());
549 if (IsBottomViewGood())
550 nChars
= std::min
<int>(nChars
, m_pwndBottom
->GetScreenChars());
551 return (nChars
==INT_MAX
) ? 0 : nChars
;
554 int CBaseView::GetAllMaxLineLength() const
557 if (IsLeftViewGood())
558 nLength
= std::max
<int>(nLength
, m_pwndLeft
->GetMaxLineLength());
559 if (IsRightViewGood())
560 nLength
= std::max
<int>(nLength
, m_pwndRight
->GetMaxLineLength());
561 if (IsBottomViewGood())
562 nLength
= std::max
<int>(nLength
, m_pwndBottom
->GetMaxLineLength());
566 int CBaseView::GetLineHeight()
568 if (m_nLineHeight
== -1)
570 if (m_nLineHeight
<= 0)
572 return m_nLineHeight
;
575 int CBaseView::GetCharWidth()
577 if (m_nCharWidth
== -1)
579 if (m_nCharWidth
<= 0)
584 int CBaseView::GetMaxLineLength()
586 if (m_nMaxLineLength
== -1)
588 m_nMaxLineLength
= 0;
589 int nLineCount
= GetLineCount();
592 m_nMaxLineLength
= GetLineLengthWithTabsConverted(0);
593 return m_nMaxLineLength
;
595 for (int i
=0; i
<nLineCount
; i
++)
597 int nActualLength
= GetLineLengthWithTabsConverted(i
);
598 if (m_nMaxLineLength
< nActualLength
)
599 m_nMaxLineLength
= nActualLength
;
602 return m_nMaxLineLength
;
605 int CBaseView::GetLineLengthWithTabsConverted(int index
)
609 if (m_pViewData
->GetCount() == 0)
611 if (m_Screen2View
.size() <= index
)
614 if (m_pMainFrame
->m_bWrapLines
)
615 sLine
= GetLineChars(index
);
618 int viewLine
= GetViewLineForScreen(index
);
619 sLine
= m_pViewData
->GetLine(viewLine
);
622 const wchar_t* pChar
= sLine
;
623 auto nLineLength
= sLine
.GetLength();
624 for (int i
= 0; i
< nLineLength
; ++i
)
630 // GetTabSize() - 1 because the tabs are already counted
631 nLineLength
= nLineLength
+ (tabCount
* (GetTabSize() - 1));
632 ASSERT(nLineLength
>= 0);
636 int CBaseView::GetLineLength(int index
)
640 if (m_pViewData
->GetCount() == 0)
642 if (m_Screen2View
.size() <= index
)
644 int viewLine
= GetViewLineForScreen(index
);
645 if (m_pMainFrame
->m_bWrapLines
)
647 int nLineLength
= GetLineChars(index
).GetLength();
648 ASSERT(nLineLength
>= 0);
651 int nLineLength
= m_pViewData
->GetLine(viewLine
).GetLength();
652 ASSERT(nLineLength
>= 0);
656 int CBaseView::GetViewLineLength(int nViewLine
) const
660 if (m_pViewData
->GetCount() <= nViewLine
)
662 int nLineLength
= m_pViewData
->GetLine(nViewLine
).GetLength();
663 ASSERT(nLineLength
>= 0);
667 int CBaseView::GetLineCount() const
671 int nLineCount
= m_Screen2View
.size();
672 ASSERT(nLineCount
>= 0);
676 int CBaseView::GetSubLineOffset(int index
)
678 return m_Screen2View
.GetSubLineOffset(index
);
681 CString
CBaseView::GetViewLineChars(int nViewLine
) const
685 if (m_pViewData
->GetCount() <= nViewLine
)
687 return m_pViewData
->GetLine(nViewLine
);
690 CString
CBaseView::GetLineChars(int index
)
694 if (m_pViewData
->GetCount() == 0)
696 if (m_Screen2View
.size() <= index
)
698 int viewLine
= GetViewLineForScreen(index
);
699 if (m_pMainFrame
->m_bWrapLines
)
701 int subLine
= GetSubLineOffset(index
);
704 if (subLine
< CountMultiLines(viewLine
))
706 return m_ScreenedViewLine
[viewLine
].SubLines
[subLine
];
711 return m_pViewData
->GetLine(viewLine
);
714 void CBaseView::CheckOtherView()
716 if (m_bOtherDiffChecked
)
718 // find out what the 'other' file is
719 m_pOtherViewData
= nullptr;
720 m_pOtherView
= nullptr;
721 if (this == m_pwndLeft
&& IsRightViewGood())
723 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
724 m_pOtherView
= m_pwndRight
;
727 if (this == m_pwndRight
&& IsLeftViewGood())
729 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
730 m_pOtherView
= m_pwndLeft
;
733 m_bOtherDiffChecked
= true;
737 void CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
, int & nStartBlock
, int & nEndBlock
)
739 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
742 DiffStates origstate
= viewData
->GetState(nLineIndex
);
744 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
745 nStartBlock
= nLineIndex
;
746 nEndBlock
= nLineIndex
;
747 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
749 DiffStates state
= viewData
->GetState(nStartBlock
- 1);
750 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
752 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
757 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
759 DiffStates state
= viewData
->GetState(nEndBlock
+ 1);
760 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
762 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
769 CString
CBaseView::GetWhitespaceString(CViewData
*viewData
, int nStartBlock
, int nEndBlock
)
771 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
774 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
775 len
+= viewData
->GetLine(i
).GetLength()+2;
778 // do not check for whitespace blocks if the line is too long, because
779 // reserving a lot of memory here takes too much time (performance hog)
780 if (len
> MAX_WHITESPACEBLOCK_SIZE
*256)
782 block
.Preallocate(len
+1);
783 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
785 block
+= viewData
->GetLine(i
);
786 block
+= m_Eols
[viewData
->GetLineEnding(i
)];
791 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
, int& blockstart
, int& blockend
)
797 if (!m_pOtherViewData
)
799 int viewLine
= GetViewLineForScreen(nLineIndex
);
801 (m_pViewData
->GetState(viewLine
) == DIFFSTATE_NORMAL
) &&
802 (m_pOtherViewData
->GetLine(viewLine
) == m_pViewData
->GetLine(viewLine
))
808 // first check whether the line itself only has whitespace changes
809 CString mine
= m_pViewData
->GetLine(viewLine
);
810 CString other
= m_pOtherViewData
->GetLine(min(viewLine
, m_pOtherViewData
->GetCount() - 1));
811 if (mine
.IsEmpty() && other
.IsEmpty())
822 FilterWhitespaces(mine
, other
);
826 int nStartBlock2
, nEndBlock2
;
827 GetWhitespaceBlock(m_pViewData
, viewLine
, blockstart
, blockend
);
828 GetWhitespaceBlock(m_pOtherViewData
, min(viewLine
, m_pOtherViewData
->GetCount() - 1), nStartBlock2
, nEndBlock2
);
829 mine
= GetWhitespaceString(m_pViewData
, blockstart
, blockend
);
834 other
= GetWhitespaceString(m_pOtherViewData
, nStartBlock2
, nEndBlock2
);
835 bIdentical
= mine
== other
;
836 FilterWhitespaces(mine
, other
);
839 return (!mine
.IsEmpty()) && (mine
== other
);
842 bool CBaseView::IsViewLineHidden(int nViewLine
)
844 return IsViewLineHidden(m_pViewData
, nViewLine
);
847 bool CBaseView::IsViewLineHidden(CViewData
* pViewData
, int nViewLine
)
849 return m_pMainFrame
->m_bCollapsed
&& (pViewData
->GetHideState(nViewLine
)!=HIDESTATE_SHOWN
);
852 int CBaseView::GetLineNumber(int index
) const
856 int viewLine
= GetViewLineForScreen(index
);
857 if (m_pViewData
->GetLineNumber(viewLine
)==DIFF_EMPTYLINENUMBER
)
859 return m_pViewData
->GetLineNumber(viewLine
);
862 int CBaseView::GetScreenLines()
864 if (m_nScreenLines
== -1)
867 GetClientRect(&rect
);
868 SCROLLBARINFO sbi
= { sizeof(sbi
) };
869 if (GetScrollBarInfo(OBJID_HSCROLL
, &sbi
))
871 // only use the scroll bar size if the info is correct and the scrollbar is visible
872 // if anything isn't proper, assume the scrollbar has a size of zero
873 // and calculate the screen lines without it.
874 if (!(sbi
.rgstate
[0] & STATE_SYSTEM_INVISIBLE
) && !(sbi
.rgstate
[0] & STATE_SYSTEM_UNAVAILABLE
))
876 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
877 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
878 if (m_nScreenLines
< 0)
880 return m_nScreenLines
;
883 // if the scroll bar is not visible, unavailable or there was an error,
884 // assume the scroll bar height is zero and return the screen lines here.
885 // Of course, that means the cache (using the member variable) won't work
886 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
887 return (rect
.Height() - HEADERHEIGHT
) / GetLineHeight();
889 return m_nScreenLines
;
892 int CBaseView::GetAllMinScreenLines() const
894 int nLines
= INT_MAX
;
895 if (IsLeftViewGood())
896 nLines
= m_pwndLeft
->GetScreenLines();
897 if (IsRightViewGood())
898 nLines
= std::min
<int>(nLines
, m_pwndRight
->GetScreenLines());
899 if (IsBottomViewGood())
900 nLines
= std::min
<int>(nLines
, m_pwndBottom
->GetScreenLines());
901 return (nLines
== INT_MAX
) || (nLines
< 0) ? 0 : nLines
;
904 int CBaseView::GetAllLineCount() const
907 if (IsLeftViewGood())
908 nLines
= m_pwndLeft
->GetLineCount();
909 if (IsRightViewGood())
910 nLines
= std::max
<int>(nLines
, m_pwndRight
->GetLineCount());
911 if (IsBottomViewGood())
912 nLines
= std::max
<int>(nLines
, m_pwndBottom
->GetLineCount());
916 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
918 if (IsLeftViewGood())
919 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
920 if (IsRightViewGood())
921 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
922 if (IsBottomViewGood())
923 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
926 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
929 si
.cbSize
= sizeof(si
);
933 si
.nPos
= m_nTopLine
;
937 EnableScrollBarCtrl(SB_VERT
, TRUE
);
938 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
943 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
945 si
.nMax
= GetAllLineCount();
946 si
.nPage
= GetAllMinScreenLines();
947 si
.nPos
= m_nTopLine
;
949 VERIFY(SetScrollInfo(SB_VERT
, &si
));
952 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
954 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
956 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
958 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
960 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
962 m_pwndLocator
->Invalidate();
965 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
967 // Note we cannot use nPos because of its 16-bit nature
969 si
.cbSize
= sizeof(si
);
971 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
973 int nPageLines
= GetScreenLines();
974 int nLineCount
= GetLineCount();
978 static LONG textwidth
= 0;
979 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
986 nNewTopLine
= nLineCount
- nPageLines
+ 1;
989 nNewTopLine
= m_nTopLine
- 1;
992 nNewTopLine
= m_nTopLine
+ 1;
995 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
998 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
1000 case SB_THUMBPOSITION
:
1001 m_ScrollTool
.Clear();
1002 nNewTopLine
= si
.nTrackPos
;
1006 nNewTopLine
= si
.nTrackPos
;
1007 if (GetFocus() == this)
1010 GetClientRect(&thumbrect
);
1011 ClientToScreen(&thumbrect
);
1014 thumbpoint
.x
= thumbrect
.right
;
1015 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
1016 m_ScrollTool
.Init(&thumbpoint
);
1019 CString sTemp
= sFormat
;
1020 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
1021 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
1023 thumbpoint
.x
-= textwidth
;
1024 int line
= GetLineNumber(nNewTopLine
);
1026 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
1028 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
1035 if (nNewTopLine
< 0)
1037 if (nNewTopLine
>= nLineCount
)
1038 nNewTopLine
= nLineCount
- 1;
1039 ScrollToLine(nNewTopLine
);
1042 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
1044 if (IsLeftViewGood())
1045 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
1046 if (IsRightViewGood())
1047 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
1048 if (IsBottomViewGood())
1049 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
1052 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
1055 si
.cbSize
= sizeof(si
);
1059 si
.nPos
= m_nOffsetChar
;
1063 EnableScrollBarCtrl(SB_HORZ
, !m_pMainFrame
->m_bWrapLines
);
1064 if (!m_pMainFrame
->m_bWrapLines
)
1066 int minScreenChars
= GetAllMinScreenChars();
1067 int maxLineLength
= GetAllMaxLineLength();
1068 if (minScreenChars
>= maxLineLength
&& m_nOffsetChar
> 0)
1073 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
1075 si
.nMax
= m_pMainFrame
->m_bWrapLines
? minScreenChars
: maxLineLength
;
1076 si
.nMax
+= GetMarginWidth()/GetCharWidth();
1077 si
.nPage
= GetScreenChars();
1078 si
.nPos
= m_nOffsetChar
;
1081 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
1084 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
1086 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
1088 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1090 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1092 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1094 m_pwndLocator
->Invalidate();
1097 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
1100 si
.cbSize
= sizeof(si
);
1102 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
1104 int nPageChars
= GetScreenChars();
1105 int nMaxLineLength
= GetMaxLineLength();
1114 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
1117 nNewOffset
= m_nOffsetChar
- 1;
1120 nNewOffset
= m_nOffsetChar
+ 1;
1123 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
1126 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
1128 case SB_THUMBPOSITION
:
1130 nNewOffset
= si
.nTrackPos
;
1136 if (nNewOffset
>= nMaxLineLength
)
1137 nNewOffset
= nMaxLineLength
- 1;
1140 ScrollToChar(nNewOffset
, TRUE
);
1143 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
1145 if (m_nOffsetChar
!= nNewOffsetChar
)
1147 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
1148 m_nOffsetChar
= nNewOffsetChar
;
1150 GetClientRect(&rcScroll
);
1151 rcScroll
.left
+= GetMarginWidth();
1152 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1153 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
1154 // update the view header
1157 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1158 InvalidateRect(&rcScroll
, FALSE
);
1160 if (bTrackScrollBar
)
1161 RecalcHorzScrollBar(TRUE
);
1163 if (m_pwndLineDiffBar
)
1164 m_pwndLineDiffBar
->Invalidate();
1168 void CBaseView::ScrollAllToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/* = TRUE */)
1171 m_pwndLeft
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1173 m_pwndRight
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1175 m_pwndBottom
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1178 void CBaseView::ScrollAllSide(int delta
)
1180 int nNewOffset
= m_nOffsetChar
;
1181 nNewOffset
+= delta
;
1182 int nMaxLineLength
= GetMaxLineLength();
1183 if (nNewOffset
>= nMaxLineLength
)
1184 nNewOffset
= nMaxLineLength
- 1;
1187 ScrollAllToChar(nNewOffset
, TRUE
);
1188 if (m_pwndLineDiffBar
)
1189 m_pwndLineDiffBar
->Invalidate();
1193 void CBaseView::ScrollSide(int delta
)
1195 int nNewOffset
= m_nOffsetChar
;
1196 nNewOffset
+= delta
;
1197 int nMaxLineLength
= GetMaxLineLength();
1198 if (nNewOffset
>= nMaxLineLength
)
1199 nNewOffset
= nMaxLineLength
- 1;
1202 ScrollToChar(nNewOffset
, TRUE
);
1203 if (m_pwndLineDiffBar
)
1204 m_pwndLineDiffBar
->Invalidate();
1208 void CBaseView::ScrollVertical(short zDelta
)
1210 const int nLineCount
= GetLineCount();
1211 int nTopLine
= m_nTopLine
;
1212 nTopLine
-= (zDelta
/30);
1215 if (nTopLine
>= nLineCount
)
1216 nTopLine
= nLineCount
- 1;
1217 ScrollToLine(nTopLine
, TRUE
);
1220 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
1222 if (m_nTopLine
!= nNewTopLine
)
1224 if (nNewTopLine
< 0)
1227 int nScrollLines
= m_nTopLine
- nNewTopLine
;
1229 m_nTopLine
= nNewTopLine
;
1231 GetClientRect(&rcScroll
);
1232 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1233 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
1235 if (bTrackScrollBar
)
1236 RecalcVertScrollBar(TRUE
);
1242 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
1244 pdc
->FillSolidRect(rect
, CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
)));
1246 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1248 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1249 HICON icon
= nullptr;
1250 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
1251 TScreenedViewLine::EIcon eIcon
= m_ScreenedViewLine
[nViewLine
].eIcon
;
1252 if (eIcon
==TScreenedViewLine::ICN_UNKNOWN
)
1254 DiffStates state
= m_pViewData
->GetState(nViewLine
);
1257 case DIFFSTATE_ADDED
:
1258 case DIFFSTATE_THEIRSADDED
:
1259 case DIFFSTATE_YOURSADDED
:
1260 case DIFFSTATE_IDENTICALADDED
:
1261 case DIFFSTATE_CONFLICTADDED
:
1262 eIcon
= TScreenedViewLine::ICN_ADD
;
1264 case DIFFSTATE_REMOVED
:
1265 case DIFFSTATE_THEIRSREMOVED
:
1266 case DIFFSTATE_YOURSREMOVED
:
1267 case DIFFSTATE_IDENTICALREMOVED
:
1268 eIcon
= TScreenedViewLine::ICN_REMOVED
;
1270 case DIFFSTATE_CONFLICTED
:
1271 eIcon
= TScreenedViewLine::ICN_CONFLICT
;
1273 case DIFFSTATE_CONFLICTED_IGNORED
:
1274 eIcon
= TScreenedViewLine::ICN_CONFLICTIGNORED
;
1276 case DIFFSTATE_EDITED
:
1277 eIcon
= TScreenedViewLine::ICN_EDIT
;
1282 bool bIdentical
= false;
1283 int blockstart
= -1;
1285 if ((state
!= DIFFSTATE_EDITED
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
, blockstart
, blockend
)))
1288 eIcon
= TScreenedViewLine::ICN_SAME
;
1290 eIcon
= TScreenedViewLine::ICN_WHITESPACEDIFF
;
1291 if (((blockstart
>= 0) && (blockend
>= 0)) && (blockstart
< blockend
))
1293 if (nViewLine
> blockstart
)
1294 Invalidate(); // redraw the upper icons since they're now changing
1295 while (blockstart
<= blockend
)
1296 m_ScreenedViewLine
[blockstart
++].eIcon
= eIcon
;
1299 if (m_pViewData
->GetMovedIndex(nViewLine
) >= 0)
1300 eIcon
= TScreenedViewLine::ICN_MOVED
;
1301 if (m_pViewData
->GetMarked(nViewLine
))
1302 eIcon
= TScreenedViewLine::ICN_MARKED
;
1303 m_ScreenedViewLine
[nViewLine
].eIcon
= eIcon
;
1307 case TScreenedViewLine::ICN_UNKNOWN
:
1308 case TScreenedViewLine::ICN_NONE
:
1310 case TScreenedViewLine::ICN_SAME
:
1311 icon
= m_hEqualIcon
;
1313 case TScreenedViewLine::ICN_EDIT
:
1314 icon
= m_hEditedIcon
;
1316 case TScreenedViewLine::ICN_WHITESPACEDIFF
:
1317 icon
= m_hWhitespaceBlockIcon
;
1319 case TScreenedViewLine::ICN_ADD
:
1320 icon
= m_hAddedIcon
;
1322 case TScreenedViewLine::ICN_CONFLICT
:
1323 icon
= m_hConflictedIcon
;
1325 case TScreenedViewLine::ICN_CONFLICTIGNORED
:
1326 icon
= m_hConflictedIgnoredIcon
;
1328 case TScreenedViewLine::ICN_REMOVED
:
1329 icon
= m_hRemovedIcon
;
1331 case TScreenedViewLine::ICN_MOVED
:
1332 icon
= m_hMovedIcon
;
1334 case TScreenedViewLine::ICN_MARKED
:
1335 icon
= m_hMarkedIcon
;
1339 int iconWidth
= GetSystemMetrics(SM_CXSMICON
);
1340 int iconHeight
= GetSystemMetrics(SM_CYSMICON
);
1343 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ CDPIAware::Instance().ScaleX(2), rect
.top
+ (rect
.Height() - iconHeight
) / 2, icon
, iconWidth
, iconHeight
, 0, nullptr, DI_NORMAL
);
1345 if ((m_bViewLinenumbers
)&&(m_nDigits
))
1347 int nSubLine
= GetSubLineOffset(nLineIndex
);
1348 bool bIsFirstSubline
= (nSubLine
== 0) || (nSubLine
== -1);
1349 CString sLinenumber
;
1350 if (bIsFirstSubline
)
1352 CString sLinenumberFormat
;
1353 int nLineNumber
= GetLineNumber(nLineIndex
);
1354 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex
)))
1356 // TODO: do not show if there is no number hidden
1357 // TODO: show number if there is only one
1358 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1359 sLinenumber
.Format(sLinenumberFormat
, (m_nDigits
>1) ? L
"↕⁞" : L
"⁞"); // alternative …
1361 else if (nLineNumber
>= 0)
1363 sLinenumberFormat
.Format(L
"%%%dd", m_nDigits
);
1364 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1366 else if (m_pMainFrame
->m_bWrapLines
)
1368 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1369 sLinenumber
.Format(sLinenumberFormat
, L
"·");
1371 if (!sLinenumber
.IsEmpty())
1373 pdc
->SetBkColor(CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
)));
1374 pdc
->SetTextColor(CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
));
1376 pdc
->SelectObject(GetFont());
1377 pdc
->ExtTextOut(rect
.left
+ iconWidth
+ CDPIAware::Instance().ScaleX(2), rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, nullptr);
1384 int CBaseView::GetMarginWidth()
1386 int marginWidth
= GetSystemMetrics(SM_CXSMICON
) + CDPIAware::Instance().ScaleX(4);
1388 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1392 int nLength
= m_pViewData
->GetCount();
1393 // find out how many digits are needed to show the highest line number
1395 sMax
.Format(L
"%d", nLength
);
1396 m_nDigits
= sMax
.GetLength();
1398 int nWidth
= GetCharWidth();
1399 marginWidth
+= (m_nDigits
* nWidth
) + CDPIAware::Instance().ScaleX(2);
1405 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1407 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1408 COLORREF crBk
, crFg
;
1409 if (IsBottomViewGood())
1411 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBk
, crFg
);
1412 crBk
= CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
));
1416 DiffStates state
= DIFFSTATE_REMOVED
;
1417 if (this == m_pwndRight
)
1419 state
= DIFFSTATE_ADDED
;
1421 CDiffColors::GetInstance().GetColors(state
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBk
, crFg
);
1423 pdc
->SetBkColor(crBk
);
1424 pdc
->FillSolidRect(textrect
, crBk
);
1426 pdc
->SetTextColor(crFg
);
1428 pdc
->SelectObject(GetFont(FALSE
, TRUE
));
1433 sViewTitle
= L
"* " + m_sWindowName
;
1437 sViewTitle
= m_sWindowName
;
1439 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1440 if (nStringLength
> rect
.Width())
1442 int offset
= std::min
<int>(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1443 sViewTitle
= m_sWindowName
.Mid(offset
);
1445 RECT titleRC
= textrect
;
1446 titleRC
.left
= std::max
<int>(rect
.left
+ (rect
.Width() - nStringLength
) / 2, 1);
1447 titleRC
.top
= rect
.top
+ (HEADERHEIGHT
/ 2);
1448 pdc
->DrawText(sViewTitle
, &titleRC
, DT_HIDEPREFIX
| DT_NOPREFIX
| DT_SINGLELINE
);
1449 if (this->GetFocus() == this)
1450 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1452 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1455 void CBaseView::OnDraw(CDC
* pDC
)
1458 GetClientRect(rcClient
);
1460 int nLineCount
= GetLineCount();
1461 int nLineHeight
= GetLineHeight();
1464 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1465 if (!m_pCacheBitmap
)
1467 m_pCacheBitmap
= new CBitmap
;
1468 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1470 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1472 DrawHeader(pDC
, rcClient
);
1476 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1477 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1478 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1479 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1481 int nCurrentLine
= m_nTopLine
;
1482 bool bBeyondFileLineCached
= false;
1483 while (rcLine
.top
< rcClient
.bottom
)
1485 if (nCurrentLine
< nLineCount
)
1487 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1488 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1489 bBeyondFileLineCached
= false;
1491 else if (!bBeyondFileLineCached
)
1493 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1494 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1495 bBeyondFileLineCached
= true;
1498 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1501 rcLine
.OffsetRect(0, nLineHeight
);
1504 cacheDC
.SelectObject(pOldBitmap
);
1508 bool CBaseView::IsStateConflicted(DiffStates state
)
1512 case DIFFSTATE_CONFLICTED
:
1513 case DIFFSTATE_CONFLICTED_IGNORED
:
1514 case DIFFSTATE_CONFLICTEMPTY
:
1515 case DIFFSTATE_CONFLICTADDED
:
1521 bool CBaseView::IsStateEmpty(DiffStates state
)
1525 case DIFFSTATE_CONFLICTEMPTY
:
1526 case DIFFSTATE_UNKNOWN
:
1527 case DIFFSTATE_EMPTY
:
1533 bool CBaseView::IsStateRemoved(DiffStates state
)
1537 case DIFFSTATE_REMOVED
:
1538 case DIFFSTATE_THEIRSREMOVED
:
1539 case DIFFSTATE_YOURSREMOVED
:
1540 case DIFFSTATE_IDENTICALREMOVED
:
1546 DiffStates
CBaseView::ResolveState(DiffStates state
)
1548 if (IsStateConflicted(state
))
1550 if (state
== DIFFSTATE_CONFLICTEMPTY
)
1551 return DIFFSTATE_CONFLICTRESOLVEDEMPTY
;
1553 return DIFFSTATE_CONFLICTRESOLVED
;
1559 bool CBaseView::IsLineEmpty(int nLineIndex
)
1561 if (m_pViewData
== 0)
1563 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1564 return IsViewLineEmpty(nViewLine
);
1567 bool CBaseView::IsViewLineEmpty(int nViewLine
)
1569 if (m_pViewData
== 0)
1571 const DiffStates state
= m_pViewData
->GetState(nViewLine
);
1572 return IsStateEmpty(state
);
1575 bool CBaseView::IsLineRemoved(int nLineIndex
)
1577 if (m_pViewData
== 0)
1579 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1580 return IsViewLineRemoved(nViewLine
);
1583 bool CBaseView::IsViewLineRemoved(int nViewLine
)
1585 if (m_pViewData
== 0)
1587 const DiffStates state
= m_pViewData
->GetState(nViewLine
);
1588 return IsStateRemoved(state
);
1591 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1594 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedDarkBk
: m_InlineAddedDarkBk
;
1595 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1598 COLORREF
CBaseView::InlineViewLineDiffColor(int nViewLine
)
1601 return IsViewLineRemoved(nViewLine
) ? m_InlineRemovedDarkBk
: m_InlineAddedDarkBk
;
1602 return IsViewLineRemoved(nViewLine
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1605 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1607 if (origin
.x
< (rc
.left
- GetCharWidth() +1))
1609 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< GetLineCount())))
1611 int viewLine
= GetViewLineForScreen(nLineIndex
);
1612 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
1615 HICON hEndingIcon
= nullptr;
1618 case EOL_CR
: hEndingIcon
= m_hLineEndingCR
; break;
1619 case EOL_CRLF
: hEndingIcon
= m_hLineEndingCRLF
; break;
1620 case EOL_LF
: hEndingIcon
= m_hLineEndingLF
; break;
1623 // If EOL style has changed, color end-of-line markers as inline differences.
1625 m_bShowInlineDiff
&& m_pOtherViewData
&&
1626 (viewLine
< m_pOtherViewData
->GetCount()) &&
1627 (ending
!= EOL_NOENDING
) &&
1628 (ending
!= m_pOtherViewData
->GetLineEnding(viewLine
) &&
1629 (m_pOtherViewData
->GetLineEnding(viewLine
) != EOL_NOENDING
))
1632 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1635 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), 0, nullptr, DI_NORMAL
);
1639 CPen
pen(PS_SOLID
, 0, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
1640 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1641 int yMiddle
= origin
.y
+ rc
.Height()/2;
1642 int xMiddle
= origin
.x
+GetCharWidth()/2;
1643 bool bMultiline
= false;
1644 if ((m_Screen2View
.size() > nLineIndex
+ 1) && (GetViewLineForScreen(nLineIndex
+ 1) == viewLine
))
1646 if (GetLineLength(nLineIndex
+1))
1650 pDC
->MoveTo(origin
.x
, yMiddle
- CDPIAware::Instance().ScaleY(2));
1651 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
- CDPIAware::Instance().ScaleY(2));
1652 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
+ CDPIAware::Instance().ScaleY(2));
1653 pDC
->LineTo(origin
.x
, yMiddle
+ CDPIAware::Instance().ScaleY(2));
1655 else if (GetLineLength(nLineIndex
) == 0)
1658 else if ((nLineIndex
> 0) && (GetViewLineForScreen(nLineIndex
-1) == viewLine
) && (GetLineLength(nLineIndex
) == 0))
1667 // arrow from top to middle+2, then left
1668 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), rc
.top
+ CDPIAware::Instance().ScaleY(1));
1669 pDC
->LineTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
);
1671 // arrow from right to left
1672 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
);
1673 pDC
->LineTo(origin
.x
, yMiddle
);
1674 pDC
->LineTo(origin
.x
+ CDPIAware::Instance().ScaleX(4), yMiddle
+ CDPIAware::Instance().ScaleY(4));
1675 pDC
->MoveTo(origin
.x
, yMiddle
);
1676 pDC
->LineTo(origin
.x
+ CDPIAware::Instance().ScaleX(4), yMiddle
- CDPIAware::Instance().ScaleY(4));
1679 // from right-upper to left then down
1680 pDC
->MoveTo(origin
.x
+ GetCharWidth() - CDPIAware::Instance().ScaleX(1), yMiddle
- CDPIAware::Instance().ScaleY(2));
1681 pDC
->LineTo(xMiddle
, yMiddle
- CDPIAware::Instance().ScaleY(2));
1682 pDC
->LineTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1683 pDC
->LineTo(xMiddle
+ CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1684 pDC
->MoveTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1685 pDC
->LineTo(xMiddle
- CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1688 // arrow from top to bottom
1689 pDC
->MoveTo(xMiddle
, rc
.top
);
1690 pDC
->LineTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1691 pDC
->LineTo(xMiddle
+ CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1692 pDC
->MoveTo(xMiddle
, rc
.bottom
- CDPIAware::Instance().ScaleY(1));
1693 pDC
->LineTo(xMiddle
- CDPIAware::Instance().ScaleX(4), rc
.bottom
- CDPIAware::Instance().ScaleY(5));
1695 case EOL_FF
: // Form Feed, U+000C
1696 case EOL_NEL
: // Next Line, U+0085
1697 case EOL_LS
: // Line Separator, U+2028
1698 case EOL_PS
: // Paragraph Separator, U+2029
1699 // draw a horizontal line at the bottom of this line
1700 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- 1, rc
.right
, rc
.bottom
, CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
));
1701 pDC
->MoveTo(origin
.x
+GetCharWidth()-1, rc
.bottom
-GetCharWidth()-2);
1702 pDC
->LineTo(origin
.x
, rc
.bottom
-2);
1703 pDC
->LineTo(origin
.x
+5, rc
.bottom
-2);
1704 pDC
->MoveTo(origin
.x
, rc
.bottom
-2);
1705 pDC
->LineTo(origin
.x
+1, rc
.bottom
-6);
1707 default: // other EOLs
1708 // arrow from top right to bottom left
1709 pDC
->MoveTo(origin
.x
+GetCharWidth()-1, rc
.bottom
-GetCharWidth());
1710 pDC
->LineTo(origin
.x
, rc
.bottom
-1);
1711 pDC
->LineTo(origin
.x
+5, rc
.bottom
-2);
1712 pDC
->MoveTo(origin
.x
, rc
.bottom
-1);
1713 pDC
->LineTo(origin
.x
+1, rc
.bottom
-6);
1719 pDC
->SelectObject(oldpen
);
1723 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1725 if (!m_bShowSelection
)
1730 if (!GetViewSelection(nSelBlockStart
, nSelBlockEnd
))
1733 const int THICKNESS
= 2;
1734 COLORREF rectcol
= 0;
1736 rectcol
= CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
);
1738 rectcol
= CTheme::Instance().GetThemeColor(GetSysColor(COLOR_GRAYTEXT
));
1740 int nViewLineIndex
= GetViewLineForScreen(nLineIndex
);
1741 int nSubLine
= GetSubLineOffset(nLineIndex
);
1742 bool bFirstLineOfViewLine
= (nSubLine
==0 || nSubLine
==-1);
1743 if ((nViewLineIndex
== nSelBlockStart
) && bFirstLineOfViewLine
)
1745 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1748 bool bLastLineOfViewLine
= (nLineIndex
+1 == m_Screen2View
.size()) || (GetViewLineForScreen(nLineIndex
) != GetViewLineForScreen(nLineIndex
+1));
1749 if ((nViewLineIndex
== nSelBlockEnd
) && bLastLineOfViewLine
)
1751 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1755 void CBaseView::DrawTextLine(
1756 CDC
* pDC
, const CRect
&rc
, int nLineIndex
, POINT
& coords
)
1758 ASSERT(nLineIndex
< GetLineCount());
1759 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1760 ASSERT(m_pViewData
&& (nViewLine
< m_pViewData
->GetCount()));
1763 rgn
.CreateRectRgn(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1764 pDC
->SelectClipRgn(&rgn
);
1765 SCOPE_EXIT
{ pDC
->SelectClipRgn(nullptr); };
1767 LineColors lineCols
= GetLineColors(nViewLine
);
1769 CString sViewLine
= GetViewLineChars(nViewLine
);
1771 if (m_bShowSelection
&& HasTextSelection())
1773 // has this line selection ?
1774 if ((m_ptSelectionViewPosStart
.y
<= nViewLine
) && (nViewLine
<= m_ptSelectionViewPosEnd
.y
))
1776 int nViewLineLength
= sViewLine
.GetLength();
1778 // first suppose the whole line is selected
1779 int selectedStart
= 0;
1780 int selectedEnd
= nViewLineLength
;
1782 // the view line is partially selected
1783 if (m_ptSelectionViewPosStart
.y
== nViewLine
)
1785 selectedStart
= m_ptSelectionViewPosStart
.x
;
1788 if (m_ptSelectionViewPosEnd
.y
== nViewLine
)
1790 selectedEnd
= m_ptSelectionViewPosEnd
.x
;
1792 // apply selection coloring
1793 // First enforce start and end point
1794 lineCols
.SplitBlock(selectedStart
);
1795 lineCols
.SplitBlock(selectedEnd
);
1796 // change color of affected parts
1797 long intenseColorScale
= m_bFocused
? 70 : 30;
1798 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(selectedStart
);
1799 for ( ; it
!= lineCols
.end() && it
->first
< selectedEnd
; ++it
)
1801 auto& second
= it
->second
;
1802 COLORREF crBk
= CAppUtils::IntenseColor(intenseColorScale
, second
.background
);
1803 if (second
.shot
== second
.background
)
1805 second
.background
= crBk
;
1806 second
.text
= CAppUtils::IntenseColor(intenseColorScale
, second
.text
);
1811 // TODO: remove duplicate from selection and mark
1812 if (!m_sMarkedWord
.IsEmpty())
1814 int nMarkLength
= m_sMarkedWord
.GetLength();
1815 //int nViewLineLength = sViewLine.GetLength();
1816 const wchar_t* text
= sViewLine
;
1817 const wchar_t* findText
= text
;
1818 while ((findText
= wcsstr(findText
, static_cast<LPCWSTR
>(m_sMarkedWord
))) != 0)
1820 int nMarkStart
= static_cast<int>(findText
- text
);
1821 int nMarkEnd
= nMarkStart
+ nMarkLength
;
1822 findText
+= nMarkLength
;
1823 ECharGroup eLeft
= GetCharGroup(sViewLine
, nMarkStart
- 1);
1824 ECharGroup eStart
= GetCharGroup(sViewLine
, nMarkStart
);
1825 if (eLeft
== eStart
)
1827 ECharGroup eRight
= GetCharGroup(sViewLine
, nMarkEnd
);
1828 ECharGroup eEnd
= GetCharGroup(sViewLine
, nMarkEnd
- 1);
1832 // First enforce start and end point
1833 lineCols
.SplitBlock(nMarkStart
);
1834 lineCols
.SplitBlock(nMarkEnd
);
1835 // change color of affected parts
1836 const long int nIntenseColorScale
= 200;
1837 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
);
1838 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
; ++it
)
1840 auto& second
= it
->second
;
1841 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1842 if (second
.shot
== second
.background
)
1844 second
.background
= crBk
;
1845 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1846 if (CTheme::Instance().IsDarkTheme())
1848 int bkGray
= (static_cast<int>(GetRValue(crBk
)) + GetGValue(crBk
) + GetBValue(crBk
)) / 3;
1849 int frGray
= (static_cast<int>(GetRValue(second
.text
)) + GetGValue(second
.text
) + GetBValue(second
.text
)) / 3;
1850 if (abs(bkGray
- frGray
) < 100)
1851 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1856 if (!m_sFindText
.IsEmpty())
1860 int nStringPos
= nMarkStart
;
1861 CString searchLine
= sViewLine
;
1863 searchLine
.MakeLower();
1864 while (StringFound(searchLine
, SearchNext
, nMarkStart
, nMarkEnd
)!=0)
1866 // First enforce start and end point
1867 lineCols
.SplitBlock(nMarkStart
+nStringPos
);
1868 lineCols
.SplitBlock(nMarkEnd
+nStringPos
);
1869 // change color of affected parts
1870 const long int nIntenseColorScale
= 30;
1871 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
+nStringPos
);
1872 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
+ nStringPos
; ++it
)
1874 auto& second
= it
->second
;
1875 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1876 if (second
.shot
== second
.background
)
1878 second
.background
= crBk
;
1879 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1881 searchLine
= searchLine
.Mid(nMarkEnd
);
1882 nStringPos
+= nMarkEnd
;
1888 // @ this point we may cache data for next line which may be same in wrapped mode
1890 int nTextOffset
= 0;
1891 int nSubline
= GetSubLineOffset(nLineIndex
);
1892 for (int n
=0; n
<nSubline
; n
++)
1894 CString sLine
= m_ScreenedViewLine
[nViewLine
].SubLines
[n
];
1895 nTextOffset
+= sLine
.GetLength();
1898 CString sLine
= GetLineChars(nLineIndex
);
1899 int nLineLength
= sLine
.GetLength();
1900 CString sLineExp
= ExpandChars(sLine
);
1901 LPCWSTR textExp
= sLineExp
;
1902 //int nLineLengthExp = sLineExp.GetLength();
1904 int nLeft
= coords
.x
;
1905 for (std::map
<int, linecolors_t
>::const_iterator itStart
= lineCols
.begin(); itStart
!= lineCols
.end(); ++itStart
)
1907 std::map
<int, linecolors_t
>::const_iterator itEnd
= itStart
;
1909 int nStart
= std::max
<int>(0, itStart
->first
- nTextOffset
);
1910 int nEnd
= nLineLength
;
1911 if (itEnd
!= lineCols
.end())
1913 nEnd
= std::min
<int>(nEnd
, itEnd
->first
- nTextOffset
);
1915 int nBlockLength
= nEnd
- nStart
;
1916 if (nBlockLength
> 0 && nEnd
>=0)
1918 auto& second
= itStart
->second
;
1919 pDC
->SetBkColor(second
.background
);
1920 pDC
->SetTextColor(second
.text
);
1921 int nEndExp
= CountExpandedChars(sLine
, nEnd
);
1922 int nTextLength
= nEndExp
- nStartExp
;
1923 LPCWSTR p_zBlockText
= textExp
+ nStartExp
;
1925 GetTextExtentPoint32(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, &Size
); // falls time-2-tme
1926 int nRight
= nLeft
+ Size
.cx
;
1927 if ((nRight
> rc
.left
) && (nLeft
< rc
.right
))
1929 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1930 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1931 // is 4094 (4095 doesn't work anymore).
1932 // So we limit the length here to that 4094 chars.
1933 // In case we're scrolled to the right, there's no need to draw the string
1934 // from way outside our window, so we also offset the drawing to the start of the window.
1935 // This reduces the string length as well.
1937 int leftcoord
= nLeft
;
1940 int fit
= nTextLength
;
1941 auto posBuffer
= std::make_unique
<int[]>(fit
);
1942 GetTextExtentExPoint(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, INT_MAX
, &fit
, posBuffer
.get(), &Size
);
1943 int lower
= 0, upper
= fit
- 1;
1946 int middle
= (upper
+ lower
+ 1) / 2;
1947 int width
= posBuffer
[middle
];
1948 if (rc
.left
- nLeft
< width
)
1952 } while (lower
< upper
);
1955 nTextLength
-= offset
;
1956 leftcoord
+= lower
> 0 ? posBuffer
[lower
- 1] : 0;
1960 drawRC
.left
= leftcoord
;
1961 drawRC
.top
= coords
.y
;
1962 pDC
->DrawText(p_zBlockText
+ offset
, min(nTextLength
, 4094), &drawRC
, DT_HIDEPREFIX
| DT_NOPREFIX
| DT_SINGLELINE
);
1963 if ((second
.shot
!= second
.background
) && (itStart
->first
== nStart
+ nTextOffset
))
1964 pDC
->FillSolidRect(nLeft
- 1, rc
.top
, 1, rc
.Height(), second
.shot
);
1968 nStartExp
= nEndExp
;
1973 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1975 if (nLineIndex
>= GetLineCount())
1977 ASSERT(nLineIndex
>= -1);
1979 if ((nLineIndex
== -1) || !m_pViewData
)
1981 // Draw line beyond the text
1982 COLORREF crBkgnd
, crText
;
1983 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
1984 pDC
->FillSolidRect(rc
, crBkgnd
);
1988 int viewLine
= GetViewLineForScreen(nLineIndex
);
1989 if (m_pMainFrame
->m_bCollapsed
)
1991 if (m_pViewData
->GetHideState(viewLine
) == HIDESTATE_MARKER
)
1993 COLORREF crBkgnd
, crText
;
1994 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
1995 pDC
->FillSolidRect(rc
, crBkgnd
);
1997 const int THICKNESS
= 2;
1998 COLORREF rectcol
= CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
);
1999 pDC
->FillSolidRect(rc
.left
, rc
.top
+ (rc
.Height()/2), rc
.Width(), THICKNESS
, rectcol
);
2000 pDC
->SetTextColor(CTheme::Instance().GetThemeColor(GetSysColor(COLOR_GRAYTEXT
)));
2001 pDC
->SetBkColor(crBkgnd
);
2003 pDC
->DrawText(L
"{...}", &rect
, DT_NOPREFIX
|DT_SINGLELINE
|DT_CENTER
);
2008 DiffStates diffState
= m_pViewData
->GetState(viewLine
);
2009 COLORREF crBkgnd
, crText
;
2010 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
2012 if (diffState
== DIFFSTATE_CONFLICTED
)
2014 // conflicted lines are shown without 'text' on them
2016 pDC
->FillSolidRect(rc
, crBkgnd
);
2017 // now draw some faint text patterns
2018 pDC
->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd
));
2019 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
2020 DrawBlockLine(pDC
, rc
, nLineIndex
);
2024 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
2025 CString sLine
= GetLineChars(nLineIndex
);
2026 if (sLine
.IsEmpty())
2028 pDC
->FillSolidRect(rc
, crBkgnd
);
2029 DrawBlockLine(pDC
, rc
, nLineIndex
);
2030 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2038 pDC
->SelectObject(GetFont(FALSE
, FALSE
));
2040 DrawTextLine(pDC
, rc
, nLineIndex
, origin
);
2042 // draw white space after the end of line
2044 if (origin
.x
> frect
.left
)
2045 frect
.left
= origin
.x
;
2046 if (frect
.right
> frect
.left
)
2047 pDC
->FillSolidRect(frect
, crBkgnd
);
2049 // draw the whitespace chars
2050 auto pszChars
= static_cast<LPCWSTR
>(sLine
);
2051 if (m_bViewWhitespace
)
2055 LPCWSTR pLastSpace
= pszChars
;
2056 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
2057 xpos
-= m_nOffsetChar
* GetCharWidth();
2059 CPen
pen(PS_SOLID
, 0, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
2066 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2067 pLastSpace
= pszChars
+ 1;
2069 int nSpaces
= GetTabSize() - nChars
% GetTabSize();
2070 if (xpos
+ nSpaces
* GetCharWidth() > 0)
2072 int xposreal
= max(xpos
, 0);
2073 if ((xposreal
> 0) || (nSpaces
> 0))
2075 CPen
* oldPen
= pDC
->SelectObject(&pen
);
2076 pDC
->MoveTo(xposreal
+ rc
.left
+ CDPIAware::Instance().ScaleX(2), y
);
2077 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(2), y
);
2078 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(6), y
- CDPIAware::Instance().ScaleY(4));
2079 pDC
->MoveTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(2), y
);
2080 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- CDPIAware::Instance().ScaleX(6), y
+ CDPIAware::Instance().ScaleY(4));
2081 pDC
->SelectObject(oldPen
);
2084 xpos
+= nSpaces
* GetCharWidth();
2090 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2091 pLastSpace
= pszChars
+ 1;
2094 const int cxWhitespace
= CDPIAware::Instance().ScaleX(2);
2095 const int cyWhitespace
= CDPIAware::Instance().ScaleY(2);
2096 // draw 2-logical pixel rectangle, like Scintilla editor.
2097 pDC
->FillSolidRect(xpos
+ rc
.left
+ GetCharWidth() / 2 - cxWhitespace
/2, y
, cxWhitespace
, cyWhitespace
, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
2099 xpos
+= GetCharWidth();
2110 DrawBlockLine(pDC
, rc
, nLineIndex
);
2111 if (origin
.x
>= rc
.left
)
2112 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2115 void CBaseView::ExpandChars(const CString
&sLine
, int nOffset
, int nCount
, CString
&line
)
2123 int nTabSize
= GetTabSize();
2125 int nActualOffset
= CountExpandedChars(sLine
, nOffset
);
2127 auto pszChars
= static_cast<LPCWSTR
>(sLine
);
2128 pszChars
+= nOffset
;
2129 int nLength
= nCount
;
2132 for (int i
=0; i
<nLength
; i
++)
2134 if (pszChars
[i
] == L
'\t')
2138 LPWSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
2140 if (nTabCount
> 0 || m_bViewWhitespace
)
2142 for (int i
=0; i
<nLength
; i
++)
2144 if (pszChars
[i
] == L
'\t')
2146 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
2149 pszBuf
[nCurPos
++] = L
' ';
2155 pszBuf
[nCurPos
] = pszChars
[i
];
2162 memcpy(pszBuf
, pszChars
, sizeof(wchar_t) * nLength
);
2165 pszBuf
[nCurPos
] = 0;
2166 line
.ReleaseBuffer();
2169 CString
CBaseView::ExpandChars(const CString
&sLine
, int nOffset
)
2172 int nLength
= sLine
.GetLength();
2173 ExpandChars(sLine
, nOffset
, nLength
, sRet
);
2177 int CBaseView::CountExpandedChars(const CString
&sLine
, int nLength
)
2179 int nTabSize
= GetTabSize();
2181 int nActualOffset
= 0;
2182 for (int i
=0; i
<nLength
; i
++)
2184 if (sLine
[i
] == L
'\t')
2185 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
2189 return nActualOffset
;
2192 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
2195 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2197 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2199 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2201 m_pwndLocator
->Invalidate();
2204 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
2206 //almost the same as ScrollAllToLine, but try to put the line in the
2207 //middle of the view, not on top
2208 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
2209 if (nNewTopLine
< 0)
2211 if (nNewTopLine
>= m_Screen2View
.size())
2212 nNewTopLine
= m_Screen2View
.size() - 1;
2214 ScrollAllToLine(nNewTopLine
);
2216 ScrollToLine(nNewTopLine
);
2219 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
2224 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
2226 if (CView::OnCreate(lpCreateStruct
) == -1)
2229 SecureZeroMemory(&m_lfBaseFont
, sizeof(m_lfBaseFont
));
2230 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2231 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2232 m_lfBaseFont
.lfHeight
= 0;
2233 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
2234 m_lfBaseFont
.lfItalic
= FALSE
;
2235 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
2236 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
2237 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
2238 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
2239 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
2244 void CBaseView::OnDestroy()
2251 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
2253 CView::OnSize(nType
, cx
, cy
);
2256 m_nScreenLines
= -1;
2257 m_nScreenChars
= -1;
2258 if (m_nLastScreenChars
!= GetScreenChars())
2260 auto oldCaretLine
= m_ptCaretViewPos
.y
;
2261 BuildAllScreen2ViewVector();
2262 m_nLastScreenChars
= m_nScreenChars
;
2263 if (m_pMainFrame
&& m_pMainFrame
->m_bWrapLines
)
2265 auto newCaretLine
= FindScreenLineForViewLine(oldCaretLine
);
2266 int nNewTopLine
= newCaretLine
- GetScreenLines() / 2;
2267 if (nNewTopLine
< 0)
2269 if (nNewTopLine
>= static_cast<int>(m_Screen2View
.size()))
2270 nNewTopLine
= static_cast<int>(m_Screen2View
.size()) - 1;
2271 ScrollToLine(nNewTopLine
);
2272 // if we're in wrap mode, the line wrapping most likely changed
2273 // and that means we have to redraw the whole window, not just the
2279 // make sure the view header is redrawn
2281 GetClientRect(&rcScroll
);
2282 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2283 InvalidateRect(&rcScroll
, FALSE
);
2288 // make sure the view header is redrawn
2290 GetClientRect(&rcScroll
);
2291 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2292 InvalidateRect(&rcScroll
, FALSE
);
2295 RecalcVertScrollBar();
2296 RecalcHorzScrollBar();
2301 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2304 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2306 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2308 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2310 m_pwndLocator
->Invalidate();
2311 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
2314 void CBaseView::OnMouseHWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2317 m_pwndLeft
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2319 m_pwndRight
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2321 m_pwndBottom
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2323 m_pwndLocator
->Invalidate();
2326 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2328 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2329 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2331 if (bControl
|| bShift
)
2333 if (m_pMainFrame
->m_bWrapLines
)
2335 // Ctrl-Wheel scrolls sideways
2336 ScrollSide(-zDelta
/30);
2340 ScrollVertical(zDelta
);
2344 void CBaseView::OnDoMouseHWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2346 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2347 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2349 if (bControl
|| bShift
)
2351 // Ctrl-H-Wheel scrolls vertical
2352 ScrollVertical(zDelta
);
2356 if (m_pMainFrame
->m_bWrapLines
)
2358 // Ctrl-Wheel scrolls sideways
2359 ScrollSide(zDelta
/30);
2363 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
2365 if (nHitTest
== HTCLIENT
)
2367 if ((m_pViewData
)&&(m_pMainFrame
->m_bCollapsed
))
2369 if (m_nMouseLine
< m_Screen2View
.size())
2371 if (m_nMouseLine
>= 0)
2373 int viewLine
= GetViewLineForScreen(m_nMouseLine
);
2374 if (viewLine
< m_pViewData
->GetCount())
2376 if (m_pViewData
->GetHideState(viewLine
) == HIDESTATE_MARKER
)
2378 ::SetCursor(::LoadCursor(nullptr, IDC_HAND
));
2385 if (m_mouseInMargin
)
2387 ::SetCursor(m_margincursor
);
2390 if (m_nMouseLine
>= 0)
2392 ::SetCursor(::LoadCursor(nullptr, IDC_IBEAM
)); // Set To Edit Cursor
2396 ::SetCursor(::LoadCursor(nullptr, IDC_ARROW
)); // Set To Arrow Cursor
2399 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
2402 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
2404 CView::OnKillFocus(pNewWnd
);
2410 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
2412 CView::OnSetFocus(pOldWnd
);
2418 int CBaseView::GetLineFromPoint(CPoint point
)
2420 ScreenToClient(&point
);
2421 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2424 void CBaseView::OnContextMenu(CPoint point
, DiffStates state
)
2427 GetClientRect(rcClient
);
2428 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+ HEADERHEIGHT
);
2430 CRect
borderrect(rcClient
.left
, rcClient
.top
+ m_nLineHeight
+ HEADERHEIGHT
, 0, rcClient
.bottom
);
2432 CPoint ptLocal
= point
;
2433 ScreenToClient(&ptLocal
);
2435 if (textrect
.PtInRect(ptLocal
) || borderrect
.PtInRect(ptLocal
))
2437 // inside the header part of the view (showing the filename)
2438 if (IsViewGood(m_pwndBottom
))
2441 if (this == m_pwndLeft
)
2444 if (!popup
.CreatePopupMenu())
2447 temp
.LoadString(IDS_HEADER_DIFFLEFTTOBASE
);
2448 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2449 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2451 m_pMainFrame
->DiffLeftToBase();
2453 if (this == m_pwndRight
)
2456 if (!popup
.CreatePopupMenu())
2459 temp
.LoadString(IDS_HEADER_DIFFRIGHTTOBASE
);
2460 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2461 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2463 m_pMainFrame
->DiffRightToBase();
2469 if (!this->IsWindowVisible())
2473 if (!popup
.CreatePopupMenu())
2476 AddContextItems(popup
, state
);
2480 int nEncodingCommandBase
= POPUPCOMMAND__LAST
;
2481 int nEolCommandBase
= nEncodingCommandBase
+_countof(uctArray
);
2485 TWhitecharsProperties oWhites
= GetWhitecharsProperties();
2486 temp
.LoadString(IDS_EDIT_TAB2SPACE
);
2487 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTabsToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_TABTOSPACES
, temp
);
2488 temp
.LoadString(IDS_EDIT_SPACE2TAB
);
2489 popup
.AppendMenu(MF_STRING
| (oWhites
.HasSpacesToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_SPACESTOTABS
, temp
);
2490 temp
.LoadString(IDS_EDIT_TRIM
);
2491 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTrailWhiteChars
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_REMOVETRAILWHITES
, temp
);
2494 if (!popupEols
.CreatePopupMenu())
2497 EOL eEolType
= GetLineEndings(oWhites
.HasMixedEols
);
2498 for (int i
= 1; i
< _countof(eolArray
); i
++)
2500 temp
= GetEolName(eolArray
[i
]);
2501 bool bChecked
= (eEolType
== eolArray
[i
]);
2502 popupEols
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEolCommandBase
+i
, temp
);
2505 temp
.LoadString(IDS_VIEWCONTEXTMENU_EOL
);
2506 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupEols
.GetSafeHmenu()), temp
);
2508 // add encoding submenu
2509 if (!popupUnicode
.CreatePopupMenu())
2511 for (int i
= 0; i
< _countof(uctArray
); i
++)
2513 temp
= CFileTextLines::GetEncodingName(uctArray
[i
]);
2514 bool bChecked
= (m_texttype
== uctArray
[i
]);
2515 popupUnicode
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEncodingCommandBase
+i
, temp
);
2517 temp
.LoadString(IDS_VIEWCONTEXTMENU_ENCODING
);
2518 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupUnicode
.GetSafeHmenu()), temp
);
2522 CompensateForKeyboard(point
);
2524 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2526 if (cmd
>= nEncodingCommandBase
&& (cmd
< nEncodingCommandBase
+ static_cast<int>(_countof(uctArray
))))
2528 SetTextType(uctArray
[cmd
-nEncodingCommandBase
]);
2530 if (cmd
>= nEolCommandBase
&& (cmd
< nEolCommandBase
+ static_cast<int>(_countof(eolArray
))))
2532 ReplaceLineEndings(eolArray
[cmd
-nEolCommandBase
]);
2537 // 2-pane view commands; target is right view
2538 case POPUPCOMMAND_USELEFTBLOCK
:
2539 m_pwndRight
->UseLeftBlock();
2541 case POPUPCOMMAND_USELEFTFILE
:
2542 m_pwndRight
->UseLeftFile();
2544 case POPUPCOMMAND_USEBOTHLEFTFIRST
:
2545 m_pwndRight
->UseBothLeftFirst();
2547 case POPUPCOMMAND_USEBOTHRIGHTFIRST
:
2548 m_pwndRight
->UseBothRightFirst();
2550 case POPUPCOMMAND_MARKBLOCK
:
2551 m_pwndRight
->MarkBlock(true);
2553 case POPUPCOMMAND_UNMARKBLOCK
:
2554 m_pwndRight
->MarkBlock(false);
2556 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS
:
2557 m_pwndRight
->LeaveOnlyMarkedBlocks();
2559 // 2-pane view multiedit commands; target is left view
2560 case POPUPCOMMAND_PREPENDFROMRIGHT
:
2561 if (!m_pwndLeft
->IsReadonly())
2562 m_pwndLeft
->UseBothRightFirst();
2564 case POPUPCOMMAND_REPLACEBYRIGHT
:
2565 if (!m_pwndLeft
->IsReadonly())
2566 m_pwndLeft
->UseRightBlock();
2568 case POPUPCOMMAND_APPENDFROMRIGHT
:
2569 if (!m_pwndLeft
->IsReadonly())
2570 m_pwndLeft
->UseBothLeftFirst();
2572 case POPUPCOMMAND_USERIGHTFILE
:
2573 m_pwndLeft
->UseRightFile();
2575 // 3-pane view commands; target is bottom view
2576 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK
:
2577 m_pwndBottom
->UseBothRightFirst();
2579 case POPUPCOMMAND_USETHEIRANDYOURBLOCK
:
2580 m_pwndBottom
->UseBothLeftFirst();
2582 case POPUPCOMMAND_USEYOURBLOCK
:
2583 m_pwndBottom
->UseRightBlock();
2585 case POPUPCOMMAND_USEYOURFILE
:
2586 m_pwndBottom
->UseRightFile();
2588 case POPUPCOMMAND_USETHEIRBLOCK
:
2589 m_pwndBottom
->UseLeftBlock();
2591 case POPUPCOMMAND_USETHEIRFILE
:
2592 m_pwndBottom
->UseLeftFile();
2594 // copy, cut and paste commands
2604 // white chars manipulations
2605 case POPUPCOMMAND_TABTOSPACES
:
2606 ConvertTabToSpaces();
2608 case POPUPCOMMAND_SPACESTOTABS
:
2611 case POPUPCOMMAND_REMOVETRAILWHITES
:
2612 RemoveTrailWhiteChars();
2617 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2621 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
2626 int nViewBlockStart
= -1;
2627 int nViewBlockEnd
= -1;
2628 GetViewSelection(nViewBlockStart
, nViewBlockEnd
);
2629 if ((point
.x
!= -1) && (point
.y
!= -1))
2631 int nLine
= GetLineFromPoint(point
)-1;
2632 if ((nLine
>= 0) && (nLine
< m_Screen2View
.size()))
2634 int nViewLine
= GetViewLineForScreen(nLine
);
2635 if (((nViewLine
< nViewBlockStart
) || (nViewBlockEnd
< nViewLine
)))
2637 ClearSelection(); // Clear text-copy selection
2639 nViewBlockStart
= nViewLine
;
2640 nViewBlockEnd
= nViewLine
;
2641 DiffStates state
= m_pViewData
->GetState(nViewLine
);
2642 while (nViewBlockStart
> 0)
2644 const DiffStates lineState
= m_pViewData
->GetState(nViewBlockStart
-1);
2645 if (!LinesInOneChange(-1, state
, lineState
))
2650 while (nViewBlockEnd
< (m_pViewData
->GetCount()-1))
2652 const DiffStates lineState
= m_pViewData
->GetState(nViewBlockEnd
+1);
2653 if (!LinesInOneChange(1, state
, lineState
))
2658 SetupAllViewSelection(nViewBlockStart
, nViewBlockEnd
);
2659 UpdateCaretPosition(SetupPoint(0, nViewLine
));
2664 // FixSelection(); fix selection range
2665 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2666 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2668 DiffStates state
= DIFFSTATE_UNKNOWN
;
2669 if (GetViewSelection(nViewBlockStart
, nViewBlockEnd
))
2671 // find a more 'relevant' state in the selection
2672 for (int i
=nViewBlockStart
; i
<=nViewBlockEnd
; ++i
)
2674 state
= m_pViewData
->GetState(i
);
2675 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
2679 OnContextMenu(point
, state
);
2682 void CBaseView::RefreshViews()
2686 m_pwndLeft
->UpdateStatusBar();
2687 m_pwndLeft
->UpdateCaret();
2688 m_pwndLeft
->Invalidate();
2692 m_pwndRight
->UpdateStatusBar();
2693 m_pwndRight
->UpdateCaret();
2694 m_pwndRight
->Invalidate();
2698 m_pwndBottom
->UpdateStatusBar();
2699 m_pwndBottom
->UpdateCaret();
2700 m_pwndBottom
->Invalidate();
2703 m_pwndLocator
->Invalidate();
2706 void CBaseView::GoToFirstDifference()
2708 SetCaretToFirstViewLine();
2709 SelectNextBlock(1, false, false);
2712 void CBaseView::GoToFirstConflict()
2714 SetCaretToFirstViewLine();
2715 SelectNextBlock(1, true, false);
2718 void CBaseView::HighlightViewLines(int nStart
, int nEnd
/* = -1 */)
2721 SetupAllViewSelection(nStart
, max(nStart
, nEnd
));
2723 UpdateCaretViewPosition(SetupPoint(0, nStart
));
2727 void CBaseView::SetupAllViewSelection(int start
, int end
)
2729 SetupViewSelection(m_pwndBottom
, start
, end
);
2730 SetupViewSelection(m_pwndLeft
, start
, end
);
2731 SetupViewSelection(m_pwndRight
, start
, end
);
2734 void CBaseView::SetupAllSelection(int start
, int end
)
2736 SetupAllViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2739 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2741 void CBaseView::SetupSelection(int start
, int end
)
2743 SetupViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2746 void CBaseView::SetupViewSelection(CBaseView
* view
, int start
, int end
)
2748 if (!IsViewGood(view
))
2750 view
->SetupViewSelection(start
, end
);
2753 void CBaseView::SetupViewSelection(int start
, int end
)
2755 // clear text selection before setting line selection ?
2756 m_nSelViewBlockStart
= start
;
2757 m_nSelViewBlockEnd
= end
;
2762 void CBaseView::OnMergePreviousconflict()
2764 SelectNextBlock(-1, true);
2767 void CBaseView::OnMergeNextconflict()
2769 SelectNextBlock(1, true);
2772 void CBaseView::OnMergeNextdifference()
2774 SelectNextBlock(1, false);
2777 void CBaseView::OnMergePreviousdifference()
2779 SelectNextBlock(-1, false);
2782 bool CBaseView::HasNextConflict()
2784 return SelectNextBlock(1, true, true, true);
2787 bool CBaseView::HasPrevConflict()
2789 return SelectNextBlock(-1, true, true, true);
2792 bool CBaseView::HasNextDiff()
2794 return SelectNextBlock(1, false, true, true);
2797 bool CBaseView::HasPrevDiff()
2799 return SelectNextBlock(-1, false, true, true);
2802 bool CBaseView::LinesInOneChange(int direction
,
2803 DiffStates initialLineState
, DiffStates currentLineState
)
2805 // Checks whether all the adjacent lines starting from the initial line
2806 // and up to the current line form the single change
2808 // First of all, if the two lines have identical states, they surely
2809 // belong to one change.
2810 if (initialLineState
== currentLineState
)
2813 // Either we move down and initial line state is "added" or "removed" and
2814 // current line state is "empty"...
2817 if (currentLineState
== DIFFSTATE_EMPTY
)
2819 if (initialLineState
== DIFFSTATE_ADDED
|| initialLineState
== DIFFSTATE_REMOVED
)
2822 if (initialLineState
== DIFFSTATE_CONFLICTADDED
&& currentLineState
== DIFFSTATE_CONFLICTEMPTY
)
2825 // ...or we move up and initial line state is "empty" and current line
2826 // state is "added" or "removed".
2829 if (initialLineState
== DIFFSTATE_EMPTY
)
2831 if (currentLineState
== DIFFSTATE_ADDED
|| currentLineState
== DIFFSTATE_REMOVED
)
2834 if (initialLineState
== DIFFSTATE_CONFLICTEMPTY
&& currentLineState
== DIFFSTATE_CONFLICTADDED
)
2840 bool CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */, bool dryrun
/* = false */)
2845 const int linesCount
= m_Screen2View
.size();
2849 int nCenterPos
= GetCaretPosition().y
;
2852 nLimit
= linesCount
;
2854 if (nCenterPos
>= linesCount
)
2855 nCenterPos
= linesCount
-1;
2857 if (bSkipEndOfCurrentBlock
)
2859 // Find end of current block
2860 const DiffStates state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2861 while (nCenterPos
!= nLimit
)
2863 const DiffStates lineState
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2864 if (!LinesInOneChange(nDirection
, state
, lineState
))
2866 nCenterPos
+= nDirection
;
2870 // Find next diff/conflict block
2871 while (nCenterPos
!= nLimit
)
2873 DiffStates linestate
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2875 (linestate
!= DIFFSTATE_NORMAL
) &&
2876 (linestate
!= DIFFSTATE_UNKNOWN
) &&
2877 (linestate
!= DIFFSTATE_FILTEREDDIFF
))
2882 ((linestate
== DIFFSTATE_CONFLICTADDED
) ||
2883 (linestate
== DIFFSTATE_CONFLICTED_IGNORED
) ||
2884 (linestate
== DIFFSTATE_CONFLICTED
) ||
2885 (linestate
== DIFFSTATE_CONFLICTEMPTY
)))
2890 nCenterPos
+= nDirection
;
2892 if (nCenterPos
== nLimit
)
2895 return (nCenterPos
!= nLimit
);
2897 // Find end of new block
2898 DiffStates state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2899 int nBlockEnd
= nCenterPos
;
2900 const int maxAllowedLine
= nLimit
-nDirection
;
2901 while (nBlockEnd
!= maxAllowedLine
)
2903 const int lineIndex
= nBlockEnd
+ nDirection
;
2904 if (lineIndex
>= linesCount
)
2906 DiffStates lineState
= m_pViewData
->GetState(GetViewLineForScreen(lineIndex
));
2907 if (!LinesInOneChange(nDirection
, state
, lineState
))
2909 nBlockEnd
+= nDirection
;
2912 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
2916 POINT ptCaretPos
= {0, nCenterPos
};
2917 SetCaretPosition(ptCaretPos
);
2920 SetupAllSelection(nCenterPos
, nBlockEnd
);
2922 SetupAllSelection(nBlockEnd
, nCenterPos
);
2924 ScrollAllToLine(nTopPos
, FALSE
);
2925 RecalcAllVertScrollBars(TRUE
);
2926 SetCaretToLineStart();
2927 EnsureCaretVisible();
2928 OnNavigateNextinlinediff();
2930 UpdateViewsCaretPosition();
2932 ShowDiffLines(nCenterPos
);
2936 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
2938 if (pNMHDR
->idFrom
!= reinterpret_cast<UINT_PTR
>(m_hWnd
))
2942 strTipText
= m_sWindowName
+ L
"\r\n" + m_sFullFilePath
;
2944 DWORD pos
= GetMessagePos();
2945 CPoint
point(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
));
2946 ScreenToClient(&point
);
2947 const int nLine
= GetButtonEventLineIndex(point
);
2951 int nViewLine
= GetViewLineForScreen(nLine
);
2952 if((m_pViewData
)&&(nViewLine
< m_pViewData
->GetCount()))
2954 auto movedIndex
= m_pViewData
->GetMovedIndex(nViewLine
);
2955 if (movedIndex
>= 0)
2957 if (m_pViewData
->IsMovedFrom(nViewLine
))
2959 strTipText
.Format(IDS_MOVED_TO_TT
, movedIndex
+1);
2963 strTipText
.Format(IDS_MOVED_FROM_TT
, movedIndex
+1);
2971 if (strTipText
.IsEmpty())
2974 // need to handle both ANSI and UNICODE versions of the message
2975 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2977 auto pTTTA
= reinterpret_cast<TOOLTIPTEXTA
*>(pNMHDR
);
2978 pTTTA
->lpszText
= m_szTip
;
2979 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2983 auto pTTTW
= reinterpret_cast<TOOLTIPTEXTW
*>(pNMHDR
);
2984 lstrcpyn(m_wszTip
, strTipText
, min(strTipText
.GetLength() + 1, static_cast<int>(_countof(m_wszTip
)) - 1));
2985 pTTTW
->lpszText
= m_wszTip
;
2988 return TRUE
; // message was handled
2991 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2994 GetClientRect(rcClient
);
2995 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2997 int marginwidth
= GetSystemMetrics(SM_CXSMICON
) + CDPIAware::Instance().ScaleX(4);
2998 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount())&&(m_nDigits
> 0))
3000 marginwidth
+= (m_nDigits
* m_nCharWidth
) + CDPIAware::Instance().ScaleX(2);
3002 CRect
borderrect(rcClient
.left
, rcClient
.top
+m_nLineHeight
+HEADERHEIGHT
, marginwidth
, rcClient
.bottom
);
3004 if (textrect
.PtInRect(point
) || borderrect
.PtInRect(point
))
3006 // inside the header part of the view (showing the filename)
3007 pTI
->hwnd
= this->m_hWnd
;
3008 this->GetClientRect(&pTI
->rect
);
3009 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
3010 pTI
->uId
= reinterpret_cast<UINT_PTR
>(m_hWnd
);
3011 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
3013 // we want multi line tooltips
3014 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
3015 if (pToolTip
->GetSafeHwnd())
3016 pToolTip
->SetMaxTipWidth(SHRT_MAX
);
3018 return (textrect
.PtInRect(point
) ? 1 : 2);
3024 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
3026 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
3027 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3034 if (this==m_pwndLeft
)
3036 if (IsViewGood(m_pwndRight
))
3038 m_pwndRight
->SetFocus();
3040 else if (IsViewGood(m_pwndBottom
))
3042 m_pwndBottom
->SetFocus();
3045 else if (this==m_pwndRight
)
3047 if (IsViewGood(m_pwndBottom
))
3049 m_pwndBottom
->SetFocus();
3051 else if (IsViewGood(m_pwndLeft
))
3053 m_pwndLeft
->SetFocus();
3056 else if (this==m_pwndBottom
)
3058 if (IsViewGood(m_pwndLeft
))
3060 m_pwndLeft
->SetFocus();
3062 else if (IsViewGood(m_pwndRight
))
3064 m_pwndRight
->SetFocus();
3071 POINT ptCaretPos
= GetCaretPosition();
3072 ptCaretPos
.y
-= GetScreenLines();
3073 ptCaretPos
.y
= max(ptCaretPos
.y
, 0l);
3074 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
3075 SetCaretPosition(ptCaretPos
);
3076 OnCaretMove(MOVELEFT
, bShift
);
3077 ShowDiffLines(ptCaretPos
.y
);
3082 POINT ptCaretPos
= GetCaretPosition();
3083 ptCaretPos
.y
+= GetScreenLines();
3084 if (ptCaretPos
.y
>= GetLineCount())
3085 ptCaretPos
.y
= GetLineCount()-1;
3086 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
3087 SetCaretPosition(ptCaretPos
);
3088 OnCaretMove(MOVERIGHT
, bShift
);
3089 ShowDiffLines(ptCaretPos
.y
);
3098 SetCaretToViewStart();
3099 m_nCaretGoalPos
= 0;
3101 AdjustSelection(MOVELEFT
);
3108 POINT ptCaretPos
= GetCaretPosition();
3109 CString sLine
= GetLineChars(ptCaretPos
.y
);
3111 while (pos
< sLine
.GetLength())
3113 if (sLine
[pos
] != ' ' && sLine
[pos
] != '\t')
3117 if (ptCaretPos
.x
== pos
)
3119 SetCaretToLineStart();
3120 m_nCaretGoalPos
= 0;
3121 OnCaretMove(MOVERIGHT
, bShift
);
3127 SetCaretAndGoalPosition(ptCaretPos
);
3128 OnCaretMove(MOVELEFT
, bShift
);
3137 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3139 ptCaretPos
.y
= GetLineCount()-1;
3140 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3141 SetCaretAndGoalPosition(ptCaretPos
);
3143 AdjustSelection(MOVERIGHT
);
3149 POINT ptCaretPos
= GetCaretPosition();
3150 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3151 if ((GetSubLineOffset(ptCaretPos
.y
) != -1) && (GetSubLineOffset(ptCaretPos
.y
) != CountMultiLines(GetViewLineForScreen(ptCaretPos
.y
))-1)) // not last screen line of view line
3155 SetCaretAndGoalPosition(ptCaretPos
);
3156 OnCaretMove(MOVERIGHT
, bShift
);
3163 if (! HasTextSelection())
3165 POINT ptCaretPos
= GetCaretPosition();
3166 if (ptCaretPos
.y
== 0 && ptCaretPos
.x
== 0)
3168 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3170 MoveCaretWordLeft();
3173 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y
))
3177 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3179 RemoveSelectedText();
3185 if (! HasTextSelection())
3189 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3190 MoveCaretWordRight();
3191 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3195 if (! MoveCaretRight())
3197 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3199 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3202 RemoveSelectedText();
3206 m_bInsertMode
= !m_bInsertMode
;
3210 if (bControl
&& m_pwndRight
)
3212 int nFirstViewLine
= 0;
3213 int nLastViewLine
= 0;
3214 if (GetViewSelection(nFirstViewLine
, nLastViewLine
))
3215 m_pwndRight
->MarkBlock(!m_pwndRight
->GetViewMarked(nFirstViewLine
));
3219 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
3222 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
3224 const int nClickedLine
= GetButtonEventLineIndex(point
);
3225 m_nLDownLine
= nClickedLine
;
3226 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
3229 ptCaretPos
.y
= nClickedLine
;
3230 int xpos2
= CalcColFromPoint(point
.x
, nClickedLine
);
3231 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, xpos2
);
3232 SetCaretAndGoalPosition(ptCaretPos
);
3234 if (nFlags
& MK_SHIFT
)
3235 AdjustSelection(MOVERIGHT
);
3239 SetupAllSelection(ptCaretPos
.y
, ptCaretPos
.y
);
3240 if (point
.x
< GetMarginWidth())
3242 // select the whole line
3243 m_ptSelectionViewPosStart
= m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3244 m_ptSelectionViewPosStart
.x
= 0;
3245 m_ptSelectionViewPosEnd
.x
= GetViewLineLength(m_ptSelectionViewPosEnd
.y
);
3249 UpdateViewsCaretPosition();
3253 CView::OnLButtonDown(nFlags
, point
);
3256 CBaseView::ECharGroup
CBaseView::GetCharGroup(wchar_t zChar
) const
3258 if (zChar
== ' ' || zChar
== '\t' )
3260 return CHG_WHITESPACE
;
3266 if (m_sWordSeparators
.Find(zChar
) >= 0)
3268 return CHG_WORDSEPARATOR
;
3270 return CHG_WORDLETTER
;
3273 void CBaseView::OnLButtonDblClk(UINT nFlags
, CPoint point
)
3275 if (m_pViewData
== 0) {
3276 CView::OnLButtonDblClk(nFlags
, point
);
3280 const int nClickedLine
= GetButtonEventLineIndex(point
);
3281 if ( nClickedLine
< 0)
3283 int nViewLine
= GetViewLineForScreen(nClickedLine
);
3284 if (point
.x
< GetMarginWidth()) // only if double clicked on the margin
3286 if((nViewLine
< m_pViewData
->GetCount())) // a double click on moved line scrolls to corresponding line
3288 if (m_pViewData
->GetMovedIndex(nViewLine
)>=0)
3290 int movedindex
= m_pViewData
->GetMovedIndex(nViewLine
);
3291 int screenLine
= FindViewLineNumber(movedindex
);
3292 int nTop
= screenLine
- GetScreenLines()/2;
3295 ScrollAllToLine(nTop
);
3296 // find and select the whole moved block
3297 int startSel
= movedindex
;
3298 int endSel
= movedindex
;
3299 while ((startSel
> 0) && (m_pOtherViewData
->GetMovedIndex(startSel
) >= 0))
3302 while ((endSel
< GetLineCount()) && (m_pOtherViewData
->GetMovedIndex(endSel
) >= 0))
3305 m_pOtherView
->SetupSelection(startSel
, endSel
);
3306 return CView::OnLButtonDblClk(nFlags
, point
);
3310 if ((m_pMainFrame
->m_bCollapsed
)&&(m_pViewData
->GetHideState(nViewLine
) == HIDESTATE_MARKER
))
3312 // a double click on a marker expands the hidden text
3314 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) != HIDESTATE_SHOWN
))
3316 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
3317 m_pwndLeft
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3318 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
3319 m_pwndRight
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3320 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
3321 m_pwndBottom
->m_pViewData
->SetLineHideState(i
, HIDESTATE_SHOWN
);
3324 BuildAllScreen2ViewVector();
3326 m_pwndLeft
->Invalidate();
3328 m_pwndRight
->Invalidate();
3330 m_pwndBottom
->Invalidate();
3335 ptCaretPos
.y
= nClickedLine
;
3336 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3337 SetCaretPosition(ptCaretPos
);
3340 POINT ptViewCarret
= GetCaretViewPosition();
3341 nViewLine
= ptViewCarret
.y
;
3342 if (nViewLine
>= GetViewCount())
3344 const CString
&sLine
= GetViewLine(nViewLine
);
3345 int nLineLength
= sLine
.GetLength();
3346 int nBasePos
= ptViewCarret
.x
;
3347 // get target char group
3348 ECharGroup eLeft
= CHG_UNKNOWN
;
3351 eLeft
= GetCharGroup(sLine
[nBasePos
-1]);
3353 ECharGroup eRight
= CHG_UNKNOWN
;
3354 if (nBasePos
< nLineLength
)
3356 eRight
= GetCharGroup(sLine
[nBasePos
]);
3358 ECharGroup eTarget
= max(eRight
, eLeft
);
3360 int nLeft
= nBasePos
;
3361 while (nLeft
> 0 && GetCharGroup(sLine
[nLeft
-1]) == eTarget
)
3366 int nRight
= nBasePos
;
3367 while (nRight
< nLineLength
&& GetCharGroup(sLine
[nRight
]) == eTarget
)
3372 m_ptSelectionViewPosStart
.x
= nLeft
;
3373 m_ptSelectionViewPosStart
.y
= nViewLine
;
3374 m_ptSelectionViewPosEnd
.x
= nRight
;
3375 m_ptSelectionViewPosEnd
.y
= nViewLine
;
3376 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
3377 SetupAllViewSelection(nViewLine
, nViewLine
);
3379 ptCaretPos
= ConvertViewPosToScreen(m_ptSelectionViewPosEnd
);
3380 UpdateViewsCaretPosition();
3384 m_sPreviousMarkedWord
= m_sMarkedWord
; // store marked word to recall in case of triple click
3385 int nMarkWidth
= max(nRight
- nLeft
, 0);
3386 m_sMarkedWord
= sLine
.Mid(m_ptSelectionViewPosStart
.x
, nMarkWidth
).Trim();
3387 if (m_sMarkedWord
.Compare(m_sPreviousMarkedWord
) == 0)
3389 m_sMarkedWord
.Empty();
3392 if (GetKeyState(VK_CONTROL
) & 0x8000)
3394 // if the ctrl key is pressed, only
3395 // mark the words in this view
3396 SetMarkedWord(m_sMarkedWord
);
3401 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3403 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3405 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3410 m_pwndLocator
->Invalidate();
3413 CView::OnLButtonDblClk(nFlags
, point
);
3416 void CBaseView::OnLButtonTrippleClick( UINT
/*nFlags*/, CPoint point
)
3418 const int nClickedLine
= GetButtonEventLineIndex(point
);
3419 if (((point
.y
- (GetLineHeight() + HEADERHEIGHT
)) / GetLineHeight()) <= 0)
3421 if (!m_sConvertedFilePath
.IsEmpty() && (GetKeyState(VK_CONTROL
)&0x8000))
3423 PCIDLIST_ABSOLUTE __unaligned pidl
= ILCreateFromPath(static_cast<LPCWSTR
>(m_sConvertedFilePath
));
3426 SHOpenFolderAndSelectItems(pidl
,0,0,0);
3427 CoTaskMemFree((LPVOID
)pidl
);
3433 ptCaretPos
.y
= nClickedLine
;
3434 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3435 SetCaretAndGoalPosition(ptCaretPos
);
3436 m_sMarkedWord
= m_sPreviousMarkedWord
; // recall previous Marked word
3438 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3440 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3442 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3444 m_ptSelectionViewPosStart
.x
= 0;
3445 m_ptSelectionViewPosStart
.y
= nClickedLine
;
3446 m_ptSelectionViewPosEnd
.x
= GetLineLength(nClickedLine
);
3447 m_ptSelectionViewPosEnd
.y
= nClickedLine
;
3448 SetupSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
3449 UpdateViewsCaretPosition();
3452 m_pwndLocator
->Invalidate();
3455 void CBaseView::OnEditCopy()
3457 CString sCopyData
= GetSelectedText();
3459 if (!sCopyData
.IsEmpty())
3461 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
3465 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
3467 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0)
3469 --m_pMainFrame
->m_nMoveMovesToIgnore
;
3470 CView::OnMouseMove(nFlags
, point
);
3473 int nMouseLine
= GetButtonEventLineIndex(point
);
3474 if (nMouseLine
< -1)
3476 m_mouseInMargin
= point
.x
< GetMarginWidth();
3478 ShowDiffLines(nMouseLine
);
3480 KillTimer(IDT_SCROLLTIMER
);
3481 if (nFlags
& MK_LBUTTON
)
3483 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3484 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3485 if (saveMouseLine
< 0)
3487 int col
= CalcColFromPoint(point
.x
, saveMouseLine
);
3488 int charIndex
= CalculateCharIndex(saveMouseLine
, col
);
3489 if (HasSelection() &&
3490 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
3492 POINT ptCaretPos
= {charIndex
, nMouseLine
};
3493 SetCaretAndGoalPosition(ptCaretPos
);
3494 AdjustSelection(MOVERIGHT
);
3498 if (m_nLDownLine
< 0)
3499 return; // no scrolling if the click started on the header
3500 if (nMouseLine
< m_nTopLine
)
3502 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3503 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3505 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3507 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3508 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3510 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3513 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3515 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3518 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3524 CView::OnMouseMove(nFlags
, point
);
3527 void CBaseView::OnLButtonUp(UINT nFlags
, CPoint point
)
3531 KillTimer(IDT_SCROLLTIMER
);
3533 __super::OnLButtonUp(nFlags
, point
);
3536 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
3538 if (nIDEvent
== IDT_SCROLLTIMER
)
3541 GetCursorPos(&point
);
3542 ScreenToClient(&point
);
3543 int nMouseLine
= GetButtonEventLineIndex(point
);
3544 if (nMouseLine
< -1)
3548 if (GetKeyState(VK_LBUTTON
)&0x8000)
3550 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3551 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3552 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
3553 if (nMouseLine
< m_nTopLine
)
3555 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3556 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3558 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3560 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3561 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3563 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3566 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3568 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3571 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3577 CView::OnTimer(nIDEvent
);
3580 void CBaseView::ShowDiffLines(int nLine
)
3582 if ((nLine
< m_nTopLine
)||(nLine
>= GetLineCount()))
3584 m_pwndLineDiffBar
->ShowLines(nLine
);
3586 m_nMouseLine
= nLine
;
3590 if ((!m_pwndRight
)||(!m_pwndLeft
))
3592 if(m_pMainFrame
->m_bOneWay
)
3595 nLine
= (nLine
> m_pwndRight
->m_Screen2View
.size() ? -1 : nLine
);
3596 nLine
= (nLine
> m_pwndLeft
->m_Screen2View
.size() ? -1 : nLine
);
3601 if (nLine
!= m_nMouseLine
)
3603 if (nLine
>= GetLineCount())
3605 m_nMouseLine
= nLine
;
3606 m_pwndLineDiffBar
->ShowLines(nLine
);
3608 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
3611 const viewdata
& CBaseView::GetEmptyLineData()
3613 static const viewdata
emptyLine(L
"", DIFFSTATE_EMPTY
, -1, EOL_NOENDING
, HIDESTATE_SHOWN
);
3617 void CBaseView::InsertViewEmptyLines(int nFirstView
, int nCount
)
3619 for (int i
= 0; i
< nCount
; i
++)
3621 InsertViewData(nFirstView
, GetEmptyLineData());
3626 void CBaseView::UpdateCaret()
3628 POINT ptCaretPos
= GetCaretPosition();
3629 ptCaretPos
.y
= std::max
<int>(std::min
<int>(ptCaretPos
.y
, GetLineCount()-1), 0);
3630 ptCaretPos
.x
= std::max
<int>(std::min
<int>(ptCaretPos
.x
, GetLineLength(ptCaretPos
.y
)), 0);
3631 SetCaretPosition(ptCaretPos
);
3633 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3636 ptCaretPos
.y
>= m_nTopLine
&&
3637 ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
3638 nCaretOffset
>= m_nOffsetChar
&&
3639 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
3641 POINT pt1
= TextToClient(ptCaretPos
);
3643 CreateSolidCaret(2, GetLineHeight());
3646 POINT pt
= { ptCaretPos
.x
+ 1, ptCaretPos
.y
};
3647 POINT pt2
= TextToClient(pt
);
3648 int width
= max(GetCharWidth(), static_cast<int>(pt2
.x
- pt1
.x
));
3649 CreateSolidCaret(width
, GetLineHeight());
3660 POINT
CBaseView::ConvertScreenPosToView(const POINT
& pt
)
3665 int nSubLine
= GetSubLineOffset(pt
.y
);
3668 for (int nScreenLine
= pt
.y
-1; nScreenLine
>= pt
.y
-nSubLine
; nScreenLine
--)
3670 ptViewPos
.x
+= GetLineChars(nScreenLine
).GetLength();
3674 ptViewPos
.y
= GetViewLineForScreen(pt
.y
);
3678 POINT
CBaseView::ConvertViewPosToScreen(const POINT
& pt
)
3681 int nViewLineLenLeft
= GetViewLineLength(pt
.y
);
3682 ptPos
.x
= min(static_cast<LONG
>(nViewLineLenLeft
), pt
.x
);
3683 ptPos
.y
= FindScreenLineForViewLine(pt
.y
);
3684 if (GetViewLineForScreen(ptPos
.y
) != pt
.y
)
3688 else if (GetSubLineOffset(ptPos
.y
) >= 0) // sublined
3690 int nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3691 while (nSubLineLength
< ptPos
.x
)
3693 ptPos
.x
-= nSubLineLength
;
3694 nViewLineLenLeft
-= nSubLineLength
;
3696 nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3698 // last pos of non last sub-line go to start of next screen line
3699 // Note: while this works correctly, it's not what a user might expect:
3700 // cursor-right when the caret is before the last char of a wrapped line
3701 // now moves the caret to the next line. But users expect the caret to
3702 // move to the right of the last char instead, and with another cursor-right
3703 // keystroke to move the caret to the next line.
3704 // Basically, this would require to handle two caret positions for the same
3705 // logical position in the line string (one on the last position of the first line,
3706 // one on the first position of the new line. For non-wrapped lines this works
3707 // because there's an 'invisible' newline char at the end of the first line.
3708 if (nSubLineLength
== ptPos
.x
&& nViewLineLenLeft
> nSubLineLength
)
3719 void CBaseView::EnsureCaretVisible()
3721 POINT ptCaretPos
= GetCaretPosition();
3722 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3724 if (ptCaretPos
.y
< m_nTopLine
)
3725 ScrollAllToLine(ptCaretPos
.y
);
3726 int screnLines
= GetScreenLines();
3729 if (ptCaretPos
.y
>= (m_nTopLine
+screnLines
)-1)
3730 ScrollAllToLine(ptCaretPos
.y
-screnLines
+2);
3731 if (nCaretOffset
< m_nOffsetChar
)
3732 ScrollAllToChar(nCaretOffset
);
3733 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
3734 ScrollAllToChar(nCaretOffset
-GetScreenChars()+1);
3738 int CBaseView::CalculateActualOffset(const POINT
& point
)
3740 int nLineIndex
= point
.y
;
3741 int nCharIndex
= point
.x
;
3742 ASSERT(nCharIndex
>= 0);
3743 CString sLine
= GetLineChars(nLineIndex
);
3744 int nLineLength
= sLine
.GetLength();
3745 return CountExpandedChars(sLine
, min(nCharIndex
, nLineLength
));
3748 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
)
3750 int nLength
= GetLineLength(nLineIndex
);
3751 int nSubLine
= GetSubLineOffset(nLineIndex
);
3754 int nViewLine
= GetViewLineForScreen(nLineIndex
);
3755 if (nViewLine
>= 0 && nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()))
3757 int nMultilineCount
= CountMultiLines(nViewLine
);
3758 if ((nMultilineCount
>0) && (nSubLine
<nMultilineCount
-1))
3764 CString Line
= GetLineChars(nLineIndex
);
3767 int nTabSize
= GetTabSize();
3768 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
3770 if (Line
.GetAt(nIndex
) == L
'\t')
3771 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
3780 * @param xpos X coordinate in CBaseView
3781 * @param lineIndex logical line index (e.g. wrap/collapse)
3783 int CBaseView::CalcColFromPoint(int xpos
, int lineIndex
)
3789 CString text
= ExpandChars(GetLineChars(lineIndex
), 0);
3790 int fit
= text
.GetLength();
3791 auto posBuffer
= std::make_unique
<int[]>(fit
);
3792 pDC
->SelectObject(GetFont()); // is this right font ?
3794 GetTextExtentExPoint(pDC
->GetSafeHdc(), text
, fit
, INT_MAX
, &fit
, posBuffer
.get(), &size
);
3796 int lower
= -1, upper
= fit
- 1;
3797 int xcheck
= xpos
- GetMarginWidth() + m_nOffsetChar
* GetCharWidth();
3800 int middle
= (upper
+ lower
+ 1) / 2;
3801 int width
= posBuffer
[middle
];
3806 } while (lower
< upper
);
3809 if (lower
< fit
- 1)
3811 int charWidth
= posBuffer
[lower
] - (lower
> 0 ? posBuffer
[lower
- 1] : 0);
3812 if (posBuffer
[lower
] - xcheck
<= charWidth
/ 2)
3818 xpos2
= (xpos
- GetMarginWidth()) / GetCharWidth() + m_nOffsetChar
;
3819 if ((xpos
% GetCharWidth()) >= (GetCharWidth()/2))
3825 POINT
CBaseView::TextToClient(const POINT
& point
)
3828 int nOffsetScreenLine
= max(0, static_cast<int>(point
.y
- m_nTopLine
));
3829 pt
.y
= nOffsetScreenLine
* GetLineHeight();
3830 pt
.x
= CalculateActualOffset(point
);
3832 int nLeft
= GetMarginWidth() - m_nOffsetChar
* GetCharWidth();
3833 CDC
* pDC
= GetDC();
3836 pDC
->SelectObject(GetFont()); // is this right font ?
3837 int nScreenLine
= nOffsetScreenLine
+ m_nTopLine
;
3838 CString sLine
= GetLineChars(nScreenLine
);
3839 ExpandChars(sLine
, 0, std::min
<int>(pt
.x
, sLine
.GetLength()), sLine
);
3840 nLeft
+= pDC
->GetTextExtent(sLine
, pt
.x
).cx
;
3843 nLeft
+= pt
.x
* GetCharWidth();
3847 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
3851 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
3853 CView::OnChar(nChar
, nRepCnt
, nFlags
);
3855 bool bControl
= !!(GetKeyState(VK_CONTROL
) & 0x8000);
3856 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3857 bool bSkipSelectionClear
= false;
3862 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
3863 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
3868 if (!m_pViewData
) // no data - nothing to do
3871 if (nChar
== VK_F16
)
3873 // generated by a ctrl+backspace - ignore.
3875 else if (nChar
==VK_TAB
&& HasTextLineSelection())
3877 // change indentation for selected lines
3880 RemoveIndentationForSelectedBlock();
3884 AddIndentationForSelectedBlock();
3886 bSkipSelectionClear
= true;
3888 else if ((nChar
> 31)||(nChar
== VK_TAB
))
3891 RemoveSelectedText();
3892 POINT ptCaretViewPos
= GetCaretViewPosition();
3893 int nViewLine
= ptCaretViewPos
.y
;
3894 if ((nViewLine
==0)&&(GetViewCount()==0))
3895 OnChar(VK_RETURN
, 0, 0);
3897 viewdata lineData
= GetViewData(nViewLine
);
3898 if (nChar
== VK_TAB
)
3900 int indentChars
= GetIndentCharsForLine(ptCaretViewPos
.x
, nViewLine
);
3901 if (indentChars
> 0)
3903 lineData
.sLine
.Insert(ptCaretViewPos
.x
, CString(L
' ', indentChars
));
3904 charCount
= indentChars
;
3907 lineData
.sLine
.Insert(ptCaretViewPos
.x
, L
'\t');
3912 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3915 if (lineData
.sLine
.GetLength() > ptCaretViewPos
.x
)
3916 lineData
.sLine
.SetAt(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3918 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3921 if (IsStateEmpty(lineData
.state
) || IsStateConflicted(lineData
.state
) || lineData
.state
== DIFFSTATE_IDENTICALREMOVED
)
3923 // if not last line set EOL
3924 for (int nCheckViewLine
= nViewLine
+1; nCheckViewLine
< GetViewCount(); nCheckViewLine
++)
3926 if (!IsViewLineEmpty(nCheckViewLine
))
3928 lineData
.ending
= m_lineendings
;
3932 // make sure previous (non empty) line have EOL set
3933 for (int nCheckViewLine
= nViewLine
-1; nCheckViewLine
> 0; nCheckViewLine
--)
3935 if (!IsViewLineEmpty(nCheckViewLine
) && GetViewState(nCheckViewLine
) != DIFFSTATE_IDENTICALREMOVED
)
3937 if (GetViewLineEnding(nCheckViewLine
) == EOL_NOENDING
)
3939 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
3945 lineData
.state
= DIFFSTATE_EDITED
;
3946 bool bNeedRenumber
= false;
3947 if (lineData
.linenumber
== -1)
3949 lineData
.linenumber
= 0;
3950 bNeedRenumber
= true;
3952 SetViewData(nViewLine
, lineData
);
3955 BuildAllScreen2ViewVector(nViewLine
);
3958 UpdateViewLineNumbers();
3960 for (int i
= 0; i
< charCount
; ++i
)
3964 else if (nChar
== 10)
3966 int nViewLine
= GetViewLineForScreen(GetCaretPosition().y
);
3967 EOL eol
= m_pViewData
->GetLineEnding(nViewLine
);
3968 EOL newEOL
= EOL_CRLF
;
3981 if (eol
==EOL_NOENDING
|| eol
==newEOL
)
3982 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3983 // to add EOL on newly edited empty line hit enter
3984 // don't store into UNDO if no change happened
3985 // and don't mark file as modified
3987 AddUndoViewLine(nViewLine
);
3988 m_pViewData
->SetLineEnding(nViewLine
, newEOL
);
3989 m_pViewData
->SetState(nViewLine
, DIFFSTATE_EDITED
);
3992 else if ((nChar
== VK_RETURN
) && !bControl
)
3994 // insert a new, fresh and empty line below the cursor
3995 RemoveSelectedText();
3997 CUndo::GetInstance().BeginGrouping();
3999 POINT ptCaretViewPos
= GetCaretViewPosition();
4000 int nViewLine
= ptCaretViewPos
.y
;
4001 int nLeft
= ptCaretViewPos
.x
;
4002 CString sLine
= GetViewLineChars(nViewLine
);
4003 CString sLineLeft
= sLine
.Left(nLeft
);
4004 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
4005 EOL eOriginalEnding
= EOL_AUTOLINE
;
4006 if (m_pViewData
->GetCount() > nViewLine
)
4007 eOriginalEnding
= GetViewLineEnding(nViewLine
);
4009 if (!sLineRight
.IsEmpty() || (eOriginalEnding
!=m_lineendings
))
4011 viewdata
newFirstLine(sLineLeft
, DIFFSTATE_EDITED
, 1, m_lineendings
, HIDESTATE_SHOWN
);
4012 SetViewData(nViewLine
, newFirstLine
);
4015 int nInsertLine
= (m_pViewData
->GetCount()==0) ? 0 : nViewLine
+ 1;
4016 viewdata
newLastLine(sLineRight
, DIFFSTATE_EDITED
, 1, eOriginalEnding
, HIDESTATE_SHOWN
);
4017 InsertViewData(nInsertLine
, newLastLine
);
4021 // adds new line everywhere except me
4022 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!=this)
4024 m_pwndLeft
->InsertViewEmptyLines(nInsertLine
, 1);
4026 if (IsViewGood(m_pwndRight
) && m_pwndRight
!=this)
4028 m_pwndRight
->InsertViewEmptyLines(nInsertLine
, 1);
4030 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!=this)
4032 m_pwndBottom
->InsertViewEmptyLines(nInsertLine
, 1);
4036 UpdateViewLineNumbers();
4038 CUndo::GetInstance().EndGrouping();
4040 BuildAllScreen2ViewVector();
4041 // move the cursor to the new line
4042 ptCaretViewPos
= SetupPoint(0, nViewLine
+1);
4043 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4046 return; // Unknown control character -- ignore it.
4047 if (!bSkipSelectionClear
)
4049 EnsureCaretVisible();
4054 void CBaseView::AddUndoViewLine(int nViewLine
, bool bAddEmptyLine
)
4057 m_AllState
.left
.AddViewLineFromView(m_pwndLeft
, nViewLine
, bAddEmptyLine
);
4058 m_AllState
.right
.AddViewLineFromView(m_pwndRight
, nViewLine
, bAddEmptyLine
);
4059 m_AllState
.bottom
.AddViewLineFromView(m_pwndBottom
, nViewLine
, bAddEmptyLine
);
4062 RecalcAllVertScrollBars();
4066 void CBaseView::AddEmptyViewLine(int nViewLineIndex
)
4070 int viewLine
= nViewLineIndex
;
4071 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
4072 if (ending
== EOL_NOENDING
)
4074 ending
= m_lineendings
;
4076 viewdata
newLine(L
"", DIFFSTATE_EDITED
, -1, ending
, HIDESTATE_SHOWN
);
4077 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
4079 CString sPartLine
= GetViewLineChars(nViewLineIndex
);
4080 int nPosx
= GetCaretPosition().x
; // should be view pos ?
4081 m_pViewData
->SetLine(viewLine
, sPartLine
.Left(nPosx
));
4082 sPartLine
= sPartLine
.Mid(nPosx
);
4083 newLine
.sLine
= sPartLine
;
4085 m_pViewData
->InsertData(viewLine
+1, newLine
);
4086 BuildAllScreen2ViewVector();
4089 void CBaseView::RemoveSelectedText()
4093 if (!HasTextSelection())
4096 // fix selection if starts or ends on empty line
4097 SetCaretViewPosition(m_ptSelectionViewPosEnd
);
4098 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4101 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
4102 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4103 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4106 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4107 if (!HasTextSelection())
4113 // We want to undo the insertion in a single step.
4115 CUndo::GetInstance().BeginGrouping();
4117 // combine first and last line
4118 viewdata oFirstLine
= GetViewData(m_ptSelectionViewPosStart
.y
);
4119 viewdata oLastLine
= GetViewData(m_ptSelectionViewPosEnd
.y
);
4120 oFirstLine
.sLine
= oFirstLine
.sLine
.Left(m_ptSelectionViewPosStart
.x
) + oLastLine
.sLine
.Mid(m_ptSelectionViewPosEnd
.x
);
4121 oFirstLine
.ending
= oLastLine
.ending
;
4122 oFirstLine
.state
= DIFFSTATE_EDITED
;
4123 SetViewData(m_ptSelectionViewPosStart
.y
, oFirstLine
);
4125 // clean up middle lines if any
4126 if (m_ptSelectionViewPosStart
.y
!= m_ptSelectionViewPosEnd
.y
)
4128 viewdata oEmptyLine
= GetEmptyLineData();
4129 for (int nViewLine
= m_ptSelectionViewPosStart
.y
+1; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
4131 SetViewData(nViewLine
, oEmptyLine
);
4135 if (CleanEmptyLines())
4137 BuildAllScreen2ViewVector(); // schedule full rebuild
4140 UpdateViewLineNumbers();
4143 SetModified(); //TODO set modified only if real data was changed
4145 CUndo::GetInstance().EndGrouping();
4147 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4148 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4152 EnsureCaretVisible();
4156 void CBaseView::PasteText()
4158 CClipboardHelper clipboardHelper
;
4159 if (!clipboardHelper
.Open(nullptr))
4162 CString sClipboardText
;
4163 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
4166 LPCSTR lpstr
= static_cast<LPCSTR
>(GlobalLock(hglb
));
4167 sClipboardText
= CString(lpstr
);
4170 hglb
= GetClipboardData(CF_UNICODETEXT
);
4173 LPCWSTR lpstr
= static_cast<LPCWSTR
>(GlobalLock(hglb
));
4174 sClipboardText
= lpstr
;
4178 if (sClipboardText
.IsEmpty())
4181 sClipboardText
.Replace(L
"\r\n", L
"\r");
4182 sClipboardText
.Replace('\n', '\r');
4184 InsertText(sClipboardText
);
4187 void CBaseView::OnCaretDown()
4189 POINT ptCaretPos
= GetCaretPosition();
4190 int nLine
= ptCaretPos
.y
;
4191 int nNextLine
= nLine
+ 1;
4192 if (nNextLine
>= GetLineCount()) // already at last line
4197 POINT ptCaretViewPos
= GetCaretViewPosition();
4198 int nViewLine
= ptCaretViewPos
.y
;
4199 int nNextViewLine
= GetViewLineForScreen(nNextLine
);
4200 if (!((nNextViewLine
== nViewLine
) && (GetSubLineOffset(nNextLine
)<CountMultiLines(nNextViewLine
)))) // not on same view line
4202 // find next suitable screen line
4203 while ((nNextViewLine
== nViewLine
) || IsViewLineHidden(nNextViewLine
))
4206 if (nNextLine
>= GetLineCount())
4210 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4213 ptCaretPos
.y
= nNextLine
;
4214 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
4215 SetCaretPosition(ptCaretPos
);
4216 OnCaretMove(MOVELEFT
);
4217 ShowDiffLines(ptCaretPos
.y
);
4220 bool CBaseView::MoveCaretLeft()
4222 POINT ptCaretViewPos
= GetCaretViewPosition();
4224 //int nViewLine = ptCaretViewPos.y;
4225 if (ptCaretViewPos
.x
== 0)
4227 int nPrevLine
= GetCaretPosition().y
;
4235 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4236 } while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
));
4237 ptCaretViewPos
= ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine
), nPrevLine
));
4238 ShowDiffLines(nPrevLine
);
4243 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4247 bool CBaseView::MoveCaretRight()
4249 POINT ptCaretViewPos
= GetCaretViewPosition();
4251 int nViewLine
= ptCaretViewPos
.y
;
4252 int nViewLineLen
= GetViewLineLength(nViewLine
);
4253 if (ptCaretViewPos
.x
>= nViewLineLen
)
4255 int nNextLine
= GetCaretPosition().y
;
4259 if (nNextLine
>= GetLineCount())
4263 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4264 } while (nNextViewLine
== nViewLine
|| IsViewLineHidden(nNextViewLine
));
4265 ptCaretViewPos
.y
= nNextViewLine
;
4266 ptCaretViewPos
.x
= 0;
4267 ShowDiffLines(nNextLine
);
4272 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4276 void CBaseView::UpdateGoalPos()
4278 m_nCaretGoalPos
= CalculateActualOffset(GetCaretPosition());
4281 void CBaseView::OnCaretLeft()
4284 OnCaretMove(MOVELEFT
);
4287 void CBaseView::OnCaretRight()
4290 OnCaretMove(MOVERIGHT
);
4293 void CBaseView::OnCaretUp()
4295 POINT ptCaretPos
= GetCaretPosition();
4296 int nLine
= ptCaretPos
.y
;
4297 if (nLine
<= 0) // already at first line
4301 int nPrevLine
= nLine
- 1;
4303 POINT ptCaretViewPos
= GetCaretViewPosition();
4304 int nViewLine
= ptCaretViewPos
.y
;
4305 int nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4306 if (nPrevViewLine
!= nViewLine
) // not on same view line
4308 // find previous suitable screen line
4309 while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
))
4316 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4319 ptCaretPos
.y
= nPrevLine
;
4320 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
);
4321 SetCaretPosition(ptCaretPos
);
4322 OnCaretMove(MOVELEFT
);
4323 ShowDiffLines(ptCaretPos
.y
);
4326 bool CBaseView::IsWordSeparator(const wchar_t ch
) const
4328 switch (GetCharGroup(ch
))
4331 case CHG_WHITESPACE
:
4332 case CHG_WORDSEPARATOR
:
4338 bool CBaseView::IsCaretAtWordBoundary()
4340 POINT ptViewCaret
= GetCaretViewPosition();
4341 CString line
= GetViewLineChars(ptViewCaret
.y
);
4343 return false; // no boundary at the empty lines
4344 if (ptViewCaret
.x
== 0)
4345 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
));
4346 if (ptViewCaret
.x
>= GetViewLineLength(ptViewCaret
.y
))
4347 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4349 IsWordSeparator(line
.GetAt(ptViewCaret
.x
)) !=
4350 IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4353 void CBaseView::UpdateViewsCaretPosition()
4355 POINT ptCaretPos
= GetCaretPosition();
4356 if (m_pwndBottom
&& m_pwndBottom
!=this)
4357 m_pwndBottom
->UpdateCaretPosition(ptCaretPos
);
4358 if (m_pwndLeft
&& m_pwndLeft
!=this)
4359 m_pwndLeft
->UpdateCaretPosition(ptCaretPos
);
4360 if (m_pwndRight
&& m_pwndRight
!=this)
4361 m_pwndRight
->UpdateCaretPosition(ptCaretPos
);
4364 void CBaseView::OnCaretWordleft()
4366 MoveCaretWordLeft();
4367 OnCaretMove(MOVELEFT
);
4370 void CBaseView::OnCaretWordright()
4372 MoveCaretWordRight();
4373 OnCaretMove(MOVERIGHT
);
4376 void CBaseView::MoveCaretWordLeft()
4378 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4383 void CBaseView::MoveCaretWordRight()
4385 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4390 void CBaseView::ClearCurrentSelection()
4392 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4393 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosStart
;
4394 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
4395 m_nSelViewBlockStart
= -1;
4396 m_nSelViewBlockEnd
= -1;
4400 void CBaseView::ClearSelection()
4403 m_pwndLeft
->ClearCurrentSelection();
4405 m_pwndRight
->ClearCurrentSelection();
4407 m_pwndBottom
->ClearCurrentSelection();
4410 void CBaseView::AdjustSelection(bool bMoveLeft
)
4412 POINT ptCaretViewPos
= GetCaretViewPosition();
4413 if (ArePointsSame(m_ptSelectionViewPosOrigin
, SetupPoint(-1, -1)))
4415 // select all have been used recently update origin
4416 m_ptSelectionViewPosOrigin
= bMoveLeft
? m_ptSelectionViewPosEnd
: m_ptSelectionViewPosStart
;
4418 if ((ptCaretViewPos
.y
< m_ptSelectionViewPosOrigin
.y
) ||
4419 (ptCaretViewPos
.y
== m_ptSelectionViewPosOrigin
.y
&& ptCaretViewPos
.x
<= m_ptSelectionViewPosOrigin
.x
))
4421 m_ptSelectionViewPosStart
= ptCaretViewPos
;
4422 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosOrigin
;
4426 m_ptSelectionViewPosStart
= m_ptSelectionViewPosOrigin
;
4427 m_ptSelectionViewPosEnd
= ptCaretViewPos
;
4430 SetupAllViewSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4435 void CBaseView::OnEditCut()
4440 RemoveSelectedText();
4444 void CBaseView::OnEditPaste()
4448 CUndo::GetInstance().BeginGrouping();
4449 RemoveSelectedText();
4451 CUndo::GetInstance().EndGrouping();
4455 void CBaseView::DeleteFonts()
4457 for (int i
=0; i
<fontsCount
; i
++)
4461 m_apFonts
[i
]->DeleteObject();
4462 delete m_apFonts
[i
];
4463 m_apFonts
[i
] = nullptr;
4468 void CBaseView::OnCaretMove(bool bMoveLeft
)
4470 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
4471 OnCaretMove(bMoveLeft
, bShift
);
4474 void CBaseView::OnCaretMove(bool bMoveLeft
, bool isShiftPressed
)
4477 AdjustSelection(bMoveLeft
);
4480 EnsureCaretVisible();
4484 void CBaseView::AddContextItems(CIconMenu
& popup
, DiffStates
/*state*/)
4486 AddCutCopyAndPaste(popup
);
4489 void CBaseView::AddCutCopyAndPaste(CIconMenu
& popup
)
4491 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4493 temp
.LoadString(IDS_EDIT_COPY
);
4494 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_COPY
, temp
);
4497 temp
.LoadString(IDS_EDIT_CUT
);
4498 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_CUT
, temp
);
4499 temp
.LoadString(IDS_EDIT_PASTE
);
4500 popup
.AppendMenu(MF_STRING
| (CAppUtils::HasClipboardFormat(CF_UNICODETEXT
)||CAppUtils::HasClipboardFormat(CF_TEXT
) ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_PASTE
, temp
);
4501 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4505 void CBaseView::CompensateForKeyboard(CPoint
& point
)
4507 // if the context menu is invoked through the keyboard, we have to use
4508 // a calculated position on where to anchor the menu on
4509 if (ArePointsSame(point
, SetupPoint(-1, -1)))
4512 GetWindowRect(&rect
);
4513 point
= rect
.CenterPoint();
4517 void CBaseView::ReleaseBitmap()
4521 m_pCacheBitmap
->DeleteObject();
4522 delete m_pCacheBitmap
;
4523 m_pCacheBitmap
= nullptr;
4527 void CBaseView::BuildMarkedWordArray()
4529 int lineCount
= GetLineCount();
4530 m_arMarkedWordLines
.clear();
4531 m_arMarkedWordLines
.reserve(lineCount
);
4532 bool bDoit
= !m_sMarkedWord
.IsEmpty();
4533 m_MarkedWordCount
= 0;
4534 for (int i
= 0; i
< lineCount
; ++i
)
4538 CString line
= GetLineChars(i
);
4540 if (!line
.IsEmpty())
4543 int nMarkStart
= -1;
4544 while ((nMarkStart
= line
.Find(m_sMarkedWord
, ++nMarkStart
)) >= 0)
4546 int nMarkEnd
= nMarkStart
+ m_sMarkedWord
.GetLength();
4547 ECharGroup eLeft
= GetCharGroup(line
, nMarkStart
- 1);
4548 ECharGroup eStart
= GetCharGroup(line
, nMarkStart
);
4549 if (eLeft
!= eStart
)
4551 ECharGroup eRight
= GetCharGroup(line
, nMarkEnd
);
4552 ECharGroup eEnd
= GetCharGroup(line
, nMarkEnd
- 1);
4556 ++m_MarkedWordCount
;
4561 m_arMarkedWordLines
.push_back(found
);
4564 m_arMarkedWordLines
.push_back(0);
4567 m_arMarkedWordLines
.push_back(0);
4571 void CBaseView::BuildFindStringArray()
4573 int lineCount
= GetLineCount();
4574 m_arFindStringLines
.clear();
4575 m_arFindStringLines
.reserve(lineCount
);
4576 bool bDoit
= !m_sFindText
.IsEmpty();
4579 for (int i
= 0; i
< lineCount
; ++i
)
4583 CString line
= GetLineChars(i
);
4585 if (!line
.IsEmpty())
4587 switch (m_pViewData
->GetState(GetViewLineForScreen(i
)))
4589 case DIFFSTATE_EMPTY
:
4590 m_arFindStringLines
.push_back(0);
4592 case DIFFSTATE_UNKNOWN
:
4593 case DIFFSTATE_NORMAL
:
4594 case DIFFSTATE_FILTEREDDIFF
:
4597 m_arFindStringLines
.push_back(0);
4600 case DIFFSTATE_REMOVED
:
4601 case DIFFSTATE_REMOVEDWHITESPACE
:
4602 case DIFFSTATE_ADDED
:
4603 case DIFFSTATE_ADDEDWHITESPACE
:
4604 case DIFFSTATE_WHITESPACE
:
4605 case DIFFSTATE_WHITESPACE_DIFF
:
4606 case DIFFSTATE_CONFLICTED
:
4607 case DIFFSTATE_CONFLICTED_IGNORED
:
4608 case DIFFSTATE_CONFLICTADDED
:
4609 case DIFFSTATE_CONFLICTEMPTY
:
4610 case DIFFSTATE_CONFLICTRESOLVED
:
4611 case DIFFSTATE_IDENTICALREMOVED
:
4612 case DIFFSTATE_IDENTICALADDED
:
4613 case DIFFSTATE_THEIRSREMOVED
:
4614 case DIFFSTATE_THEIRSADDED
:
4615 case DIFFSTATE_YOURSREMOVED
:
4616 case DIFFSTATE_YOURSADDED
:
4617 case DIFFSTATE_EDITED
:
4620 line
= line
.MakeLower();
4624 while (StringFound(line
, SearchNext
, s
, e
))
4630 m_arFindStringLines
.push_back(match
);
4634 m_arFindStringLines
.push_back(0);
4638 m_arFindStringLines
.push_back(0);
4641 m_arFindStringLines
.push_back(0);
4646 bool CBaseView::GetInlineDiffPositions(int nViewLine
, std::vector
<inlineDiffPos
>& positions
)
4648 if (!m_bShowInlineDiff
)
4650 if (m_pwndBottom
&& !(m_pwndBottom
->IsHidden()))
4653 if (!m_pViewData
|| m_pViewData
->GetCount() <= nViewLine
)
4655 const CString
&sLine
= m_pViewData
->GetLine(nViewLine
);
4656 if (sLine
.IsEmpty())
4660 if (!m_pOtherViewData
)
4663 const CString
&sDiffLine
= m_pOtherViewData
->GetLine(nViewLine
);
4664 if (sDiffLine
.IsEmpty())
4667 svn_diff_t
* diff
= nullptr;
4668 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
4669 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
4670 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
4671 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
4674 size_t lineoffset
= 0;
4675 size_t position
= 0;
4678 if (this == m_pwndRight
)
4680 apr_off_t nTmp
= diff
->modified_length
;
4681 diff
->modified_length
= diff
->original_length
;
4682 diff
->original_length
= nTmp
;
4684 nTmp
= diff
->modified_start
;
4685 diff
->modified_start
= diff
->original_start
;
4686 diff
->original_start
= nTmp
;
4688 apr_off_t len
= diff
->original_length
;
4689 size_t oldpos
= position
;
4691 for (apr_off_t i
= 0; i
< len
; ++i
)
4693 position
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
4697 if (diff
->type
== svn_diff__type_diff_modified
)
4702 positions
.push_back(p
);
4708 return !positions
.empty();
4711 void CBaseView::OnNavigateNextinlinediff()
4714 if (GetNextInlineDiff(nX
))
4716 POINT ptCaretViewPos
= GetCaretViewPosition();
4717 ptCaretViewPos
.x
= nX
;
4718 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4719 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4720 EnsureCaretVisible();
4724 void CBaseView::OnNavigatePrevinlinediff()
4727 if (GetPrevInlineDiff(nX
))
4729 POINT ptCaretViewPos
= GetCaretViewPosition();
4730 ptCaretViewPos
.x
= nX
;
4731 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4732 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4733 EnsureCaretVisible();
4737 bool CBaseView::HasNextInlineDiff()
4740 return GetNextInlineDiff(nPos
);
4743 bool CBaseView::GetNextInlineDiff(int & nPos
)
4745 POINT ptCaretViewPos
= GetCaretViewPosition();
4746 std::vector
<inlineDiffPos
> positions
;
4747 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4749 for (auto it
= positions
.cbegin(); it
!= positions
.cend(); ++it
)
4751 if (it
->start
> ptCaretViewPos
.x
)
4753 nPos
= static_cast<LONG
>(it
->start
);
4756 if (it
->end
> ptCaretViewPos
.x
)
4758 nPos
= static_cast<LONG
>(it
->end
);
4766 bool CBaseView::HasPrevInlineDiff()
4769 return GetPrevInlineDiff(nPos
);
4772 bool CBaseView::GetPrevInlineDiff(int & nPos
)
4774 POINT ptCaretViewPos
= GetCaretViewPosition();
4775 std::vector
<inlineDiffPos
> positions
;
4776 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4778 for (auto it
= positions
.crbegin(); it
!= positions
.crend(); ++it
)
4780 if ( it
->end
< ptCaretViewPos
.x
)
4782 nPos
= static_cast<LONG
>(it
->end
);
4785 if ( it
->start
< ptCaretViewPos
.x
)
4787 nPos
= static_cast<LONG
>(it
->start
);
4795 CBaseView
* CBaseView::GetFirstGoodView()
4797 if (IsViewGood(m_pwndLeft
))
4799 if (IsViewGood(m_pwndRight
))
4801 if (IsViewGood(m_pwndBottom
))
4802 return m_pwndBottom
;
4806 void CBaseView::BuildAllScreen2ViewVector()
4808 CBaseView
* p_pwndView
= GetFirstGoodView();
4811 m_Screen2View
.ScheduleFullRebuild(p_pwndView
->m_pViewData
);
4815 void CBaseView::BuildAllScreen2ViewVector(int nViewLine
)
4817 BuildAllScreen2ViewVector(nViewLine
, nViewLine
);
4820 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine
, int nLastViewLine
)
4822 CBaseView
* p_pwndView
= GetFirstGoodView();
4825 m_Screen2View
.ScheduleRangeRebuild(p_pwndView
->m_pViewData
, nFirstViewLine
, nLastViewLine
);
4829 void CBaseView::UpdateViewLineNumbers()
4831 int nLineNumber
= 0;
4832 int nViewLineCount
= GetViewCount();
4833 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; nViewLine
++)
4835 int oldLine
= GetViewLineNumber(nViewLine
);
4837 SetViewLineNumber(nViewLine
, nLineNumber
++);
4842 int CBaseView::CleanEmptyLines()
4844 int nRemovedCount
= 0;
4845 int nViewLineCount
= GetViewCount();
4846 bool bCheckLeft
= IsViewGood(m_pwndLeft
);
4847 bool bCheckRight
= IsViewGood(m_pwndRight
);
4848 bool bCheckBottom
= IsViewGood(m_pwndBottom
);
4849 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; )
4851 bool bAllEmpty
= true;
4852 bAllEmpty
&= !bCheckLeft
|| IsStateEmpty(m_pwndLeft
->GetViewState(nViewLine
));
4853 bAllEmpty
&= !bCheckRight
|| IsStateEmpty(m_pwndRight
->GetViewState(nViewLine
));
4854 bAllEmpty
&= !bCheckBottom
|| IsStateEmpty(m_pwndBottom
->GetViewState(nViewLine
));
4859 m_pwndLeft
->RemoveViewData(nViewLine
);
4863 m_pwndRight
->RemoveViewData(nViewLine
);
4867 m_pwndBottom
->RemoveViewData(nViewLine
);
4869 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4879 return nRemovedCount
;
4882 int CBaseView::FindScreenLineForViewLine( int viewLine
)
4884 return m_Screen2View
.FindScreenLineForViewLine(viewLine
);
4887 int CBaseView::CountMultiLines( int nViewLine
)
4889 if (m_ScreenedViewLine
.empty())
4890 return 0; // in case the view is completely empty
4892 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4894 if (m_ScreenedViewLine
[nViewLine
].bSublinesSet
)
4896 return static_cast<int>(m_ScreenedViewLine
[nViewLine
].SubLines
.size());
4899 auto multiLines
= CStringUtils::WordWrap(m_pViewData
->GetLine(nViewLine
), GetScreenChars() - 1, GetTabSize());
4901 TScreenedViewLine oScreenedLine
;
4902 bool subLinesSet
= true;
4903 if (m_pMainFrame
->m_bWrapLines
)
4906 CFont
* pOldFont
= pDC
->SelectObject(GetFont());
4908 for (const auto& line
: multiLines
)
4910 if (line
.GetLength())
4912 // we use the 'X' char to determine the char width,
4913 // but e.g. chinese chars are much wider. To make sure
4914 // that we wrap correctly, we calculate the average char width
4915 // here by using the real line text
4916 const CSize szCharExt
= pDC
->GetTextExtent(line
);
4917 if (szCharExt
.cx
/ line
.GetLength() > m_nCharWidth
)
4919 m_nCharWidth
= szCharExt
.cx
/ line
.GetLength();
4920 subLinesSet
= false;
4923 oScreenedLine
.SubLines
.push_back(line
);
4925 pDC
->SelectObject(pOldFont
);
4930 for (const auto& line
: multiLines
)
4932 oScreenedLine
.SubLines
.push_back(line
);
4936 oScreenedLine
.bSublinesSet
= subLinesSet
;
4937 m_ScreenedViewLine
[nViewLine
] = oScreenedLine
;
4939 return CountMultiLines(nViewLine
);
4942 /// prepare inline diff cache
4943 LineColors
& CBaseView::GetLineColors(int nViewLine
)
4945 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4947 if (m_bWhitespaceInlineDiffs
)
4949 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
)
4950 return m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
;
4954 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSet
)
4955 return m_ScreenedViewLine
[nViewLine
].lineColors
;
4958 LineColors oLineColors
;
4959 // set main line color
4960 COLORREF crBkgnd
, crText
;
4961 DiffStates diffState
= m_pViewData
->GetState(nViewLine
);
4962 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
4963 oLineColors
.SetColor(0, crText
, crBkgnd
);
4966 if (!m_bShowInlineDiff
)
4969 if (((diffState
== DIFFSTATE_NORMAL
) || (diffState
== DIFFSTATE_FILTEREDDIFF
)) && (!m_bWhitespaceInlineDiffs
))
4972 CString sLine
= GetViewLineChars(nViewLine
);
4973 if (sLine
.IsEmpty())
4980 case DIFFSTATE_ADDED
:
4982 if ((nViewLine
> 0) && (m_pViewData
->GetState(nViewLine
- 1) == DIFFSTATE_REMOVED
))
4983 sDiffLine
= GetViewLineChars(nViewLine
- 1);
4986 case DIFFSTATE_REMOVED
:
4988 if (((nViewLine
+ 1) < m_pViewData
->GetCount()) && (m_pViewData
->GetState(nViewLine
+ 1) == DIFFSTATE_ADDED
))
4989 sDiffLine
= GetViewLineChars(nViewLine
+ 1);
4995 sDiffLine
= m_pOtherView
->GetViewLineChars(nViewLine
);
4996 if (sDiffLine
.IsEmpty())
4999 svn_diff_t
* diff
= nullptr;
5000 if (sLine
.GetLength() > static_cast<int>(m_nInlineDiffMaxLineLength
))
5002 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
5003 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
5004 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
5005 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
) || !diff
->next
)
5009 int nTextStartOffset
= 0;
5010 std::map
<int, COLORREF
> removedPositions
;
5013 if (this == m_pwndRight
)
5015 apr_off_t nTmp
= diff
->modified_length
;
5016 diff
->modified_length
= diff
->original_length
;
5017 diff
->original_length
= nTmp
;
5019 nTmp
= diff
->modified_start
;
5020 diff
->modified_start
= diff
->original_start
;
5021 diff
->original_start
= nTmp
;
5023 apr_off_t len
= diff
->original_length
;
5025 size_t nTextLength
= 0;
5026 for (int i
= 0; i
< len
; ++i
)
5028 nTextLength
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
5031 bool bInlineDiff
= (diff
->type
== svn_diff__type_diff_modified
);
5033 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
5034 if ((m_bShowInlineDiff
)&&(bInlineDiff
))
5036 crBkgnd
= InlineViewLineDiffColor(nViewLine
);
5038 else if (m_pOtherView
)
5040 crBkgnd
= m_bDark
? m_ModifiedDarkBk
: m_ModifiedBk
;
5043 if (len
< diff
->modified_length
)
5045 removedPositions
[nTextStartOffset
] = m_bDark
? m_InlineRemovedDarkBk
: m_InlineRemovedBk
;
5047 oLineColors
.SetColor(nTextStartOffset
, crText
, crBkgnd
);
5049 nTextStartOffset
+= static_cast<int>(nTextLength
);
5052 for (std::map
<int, COLORREF
>::const_iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
5054 oLineColors
.AddShotColor(it
->first
, it
->second
);
5056 } while (false); // error catch
5058 if (!m_bWhitespaceInlineDiffs
)
5060 m_ScreenedViewLine
[nViewLine
].lineColors
= oLineColors
;
5061 m_ScreenedViewLine
[nViewLine
].bLineColorsSet
= true;
5065 m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
= oLineColors
;
5066 m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
= true;
5069 return GetLineColors(nViewLine
);
5072 void CBaseView::OnEditSelectall()
5076 int nLastViewLine
= m_pViewData
->GetCount()-1;
5077 if (nLastViewLine
< 0)
5079 SetupAllViewSelection(0, nLastViewLine
);
5081 CString sLine
= GetViewLineChars(nLastViewLine
);
5082 m_ptSelectionViewPosStart
= SetupPoint(0, 0);
5083 m_ptSelectionViewPosEnd
= SetupPoint(sLine
.GetLength(), nLastViewLine
);
5084 m_ptSelectionViewPosOrigin
= SetupPoint(-1, -1);
5089 void CBaseView::FilterWhitespaces(CString
& first
, CString
& second
)
5091 FilterWhitespaces(first
);
5092 FilterWhitespaces(second
);
5095 void CBaseView::FilterWhitespaces(CString
& line
)
5103 int CBaseView::GetButtonEventLineIndex(const POINT
& point
)
5105 if (point
.y
< (GetLineHeight() + HEADERHEIGHT
))
5107 const int nLineFromTop
= (point
.y
- HEADERHEIGHT
) / GetLineHeight();
5108 int nEventLine
= nLineFromTop
+ m_nTopLine
;
5109 nEventLine
--; //we need the index
5114 BOOL
CBaseView::PreTranslateMessage(MSG
* pMsg
)
5116 if (RelayTrippleClick(pMsg
))
5118 return CView::PreTranslateMessage(pMsg
);
5122 void CBaseView::ResetUndoStep()
5127 void CBaseView::SaveUndoStep()
5129 if (!m_AllState
.IsEmpty())
5131 CUndo::GetInstance().AddState(m_AllState
, GetCaretViewPosition());
5136 void CBaseView::SetTheme(bool bDark
)
5138 m_bDark
= bDark
|| CTheme::Instance().IsHighContrastModeDark();
5139 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), m_bDark
);
5141 ModifyStyleEx(WS_EX_CLIENTEDGE
, 0);
5143 ModifyStyleEx(0, WS_EX_CLIENTEDGE
);
5144 CDiffColors::GetInstance().LoadRegistry();
5145 BuildAllScreen2ViewVector();
5146 if (IsWindow(GetSafeHwnd()))
5150 if (FAILED(SetWindowTheme(GetSafeHwnd(), L
"DarkMode_Explorer", nullptr)))
5151 SetWindowTheme(GetSafeHwnd(), L
"Explorer", nullptr);
5154 SetWindowTheme(GetSafeHwnd(), L
"Explorer", nullptr);
5157 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseMerge\\Colors\\Whitespace", CTheme::Instance().GetThemeColor(GetSysColor(COLOR_3DSHADOW
)));
5160 void CBaseView::InsertViewData( int index
, const CString
& sLine
, DiffStates state
, int linenumber
, EOL ending
, HIDESTATE hide
, int movedline
)
5162 m_pState
->addedlines
.push_back(index
);
5163 m_pViewData
->InsertData(index
, sLine
, state
, linenumber
, ending
, hide
, movedline
);
5166 void CBaseView::InsertViewData( int index
, const viewdata
& data
)
5168 m_pState
->addedlines
.push_back(index
);
5169 m_pViewData
->InsertData(index
, data
);
5172 void CBaseView::RemoveViewData( int index
)
5174 m_pState
->removedlines
[index
] = m_pViewData
->GetData(index
);
5175 m_pViewData
->RemoveData(index
);
5178 void CBaseView::SetViewData( int index
, const viewdata
& data
)
5180 m_pState
->replacedlines
[index
] = m_pViewData
->GetData(index
);
5181 m_pViewData
->SetData(index
, data
);
5184 void CBaseView::SetViewState( int index
, DiffStates state
)
5186 m_pState
->linestates
[index
] = m_pViewData
->GetState(index
);
5187 m_pViewData
->SetState(index
, state
);
5190 void CBaseView::SetViewLine( int index
, const CString
& sLine
)
5192 m_pState
->difflines
[index
] = m_pViewData
->GetLine(index
);
5193 m_pViewData
->SetLine(index
, sLine
);
5196 void CBaseView::SetViewLineNumber( int index
, int linenumber
)
5198 int oldLineNumber
= m_pViewData
->GetLineNumber(index
);
5199 if (oldLineNumber
!= linenumber
) {
5200 m_pState
->linelines
[index
] = oldLineNumber
;
5201 m_pViewData
->SetLineNumber(index
, linenumber
);
5205 void CBaseView::SetViewLineEnding( int index
, EOL ending
)
5207 m_pState
->linesEOL
[index
] = m_pViewData
->GetLineEnding(index
);
5208 m_pViewData
->SetLineEnding(index
, ending
);
5211 void CBaseView::SetViewMarked( int index
, bool marked
)
5213 m_pState
->markedlines
[index
] = m_pViewData
->GetMarked(index
);
5214 m_pViewData
->SetMarked(index
, marked
);
5218 BOOL
CBaseView::GetViewSelection( int& start
, int& end
) const
5222 start
= m_nSelViewBlockStart
;
5223 end
= m_nSelViewBlockEnd
;
5229 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine
)
5231 RebuildIfNecessary();
5232 if ((size() <= screenLine
) || (screenLine
< 0))
5234 return m_Screen2View
[screenLine
].nViewLine
;
5237 int CBaseView::Screen2View::size()
5239 RebuildIfNecessary();
5240 return static_cast<int>(m_Screen2View
.size());
5243 int CBaseView::Screen2View::GetSubLineOffset( int screenLine
)
5245 RebuildIfNecessary();
5246 if (size() <= screenLine
)
5248 return m_Screen2View
[screenLine
].nViewSubLine
;
5252 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5254 void CBaseView::Screen2View::RebuildIfNecessary()
5257 return; // rebuild not necessary
5259 FixScreenedCacheSize(m_pwndLeft
);
5260 FixScreenedCacheSize(m_pwndRight
);
5261 FixScreenedCacheSize(m_pwndBottom
);
5264 for (auto it
= m_RebuildRanges
.cbegin(); it
!= m_RebuildRanges
.cend(); ++it
)
5266 ResetScreenedViewLineCache(m_pwndLeft
, *it
);
5267 ResetScreenedViewLineCache(m_pwndRight
, *it
);
5268 ResetScreenedViewLineCache(m_pwndBottom
, *it
);
5273 ResetScreenedViewLineCache(m_pwndLeft
);
5274 ResetScreenedViewLineCache(m_pwndRight
);
5275 ResetScreenedViewLineCache(m_pwndBottom
);
5277 m_RebuildRanges
.clear();
5280 size_t OldSize
= m_Screen2View
.size();
5281 m_Screen2View
.clear();
5282 m_Screen2View
.reserve(OldSize
); // guess same size
5283 for (int i
= 0; i
< m_pViewData
->GetCount(); ++i
)
5285 if (m_pMainFrame
->m_bCollapsed
)
5287 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) == HIDESTATE_HIDDEN
))
5289 if (!(i
< m_pViewData
->GetCount()))
5292 TScreenLineInfo oLineInfo
;
5293 oLineInfo
.nViewLine
= i
;
5294 oLineInfo
.nViewSubLine
= -1; // no wrap
5295 if (m_pMainFrame
->m_bWrapLines
&& !IsViewLineHidden(m_pViewData
, i
))
5298 if (IsLeftViewGood())
5299 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndLeft
->CountMultiLines(i
));
5300 if (IsRightViewGood())
5301 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndRight
->CountMultiLines(i
));
5302 if (IsBottomViewGood())
5303 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndBottom
->CountMultiLines(i
));
5304 for (int l
= 0; l
< (nMaxLines
-1); ++l
)
5306 oLineInfo
.nViewSubLine
++;
5307 m_Screen2View
.push_back(oLineInfo
);
5309 oLineInfo
.nViewSubLine
++;
5311 m_Screen2View
.push_back(oLineInfo
);
5313 m_pViewData
= nullptr;
5315 if (IsLeftViewGood())
5316 m_pwndLeft
->BuildMarkedWordArray();
5317 if (IsRightViewGood())
5318 m_pwndRight
->BuildMarkedWordArray();
5319 if (IsBottomViewGood())
5320 m_pwndBottom
->BuildMarkedWordArray();
5322 RecalcAllVertScrollBars();
5323 RecalcAllHorzScrollBars();
5326 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine
)
5328 RebuildIfNecessary();
5330 int nScreenLineCount
= static_cast<int>(m_Screen2View
.size());
5333 if (nScreenLineCount
>16)
5335 // for enough long data search for last screen
5336 // with viewline less than one we are looking for
5337 // use approximate method (based on) binary search using asymmetric start point
5338 // in form 2**n (determined as MSB of length) to go around division and rounding;
5339 // this effectively looks for bit values from MSB to LSB
5342 //GetMostSignificantBitValue
5343 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5344 nTestBit
= nScreenLineCount
;
5345 nTestBit
|= nTestBit
>>1;
5346 nTestBit
|= nTestBit
>>2;
5347 nTestBit
|= nTestBit
>>4;
5348 nTestBit
|= nTestBit
>>8;
5349 nTestBit
|= nTestBit
>>16;
5350 nTestBit
^= (nTestBit
>>1);
5354 int nTestPos
= nPos
| nTestBit
;
5355 if (nTestPos
< nScreenLineCount
&& m_Screen2View
[nTestPos
].nViewLine
< viewLine
)
5362 while (nPos
< nScreenLineCount
&& m_Screen2View
[nPos
].nViewLine
< viewLine
)
5370 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData
* pViewData
) {
5373 m_pViewData
= pViewData
;
5376 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData
* pViewData
, int nFirstViewLine
, int nLastViewLine
)
5381 m_pViewData
= pViewData
;
5383 TRebuildRange Range
;
5384 Range
.FirstViewLine
=nFirstViewLine
;
5385 Range
.LastViewLine
=nLastViewLine
;
5386 m_RebuildRanges
.push_back(Range
);
5389 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView
* pwndView
)
5391 if (!IsViewGood(pwndView
))
5395 const int nOldSize
= static_cast<int>(pwndView
->m_ScreenedViewLine
.size());
5396 const int nViewCount
= std::max
<int>(pwndView
->GetViewCount(), 0);
5397 if (nOldSize
== nViewCount
)
5401 pwndView
->m_ScreenedViewLine
.resize(nViewCount
);
5405 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
) const
5407 if (!IsViewGood(pwndView
))
5411 TRebuildRange Range
={0, pwndView
->GetViewCount()-1};
5412 ResetScreenedViewLineCache(pwndView
, Range
);
5416 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
, const TRebuildRange
& Range
) const
5418 if (!IsViewGood(pwndView
))
5422 if (Range
.LastViewLine
== -1)
5426 ASSERT(Range
.FirstViewLine
>= 0);
5427 ASSERT(Range
.LastViewLine
< pwndView
->GetViewCount());
5428 for (int i
= Range
.FirstViewLine
; i
<= Range
.LastViewLine
; i
++)
5430 pwndView
->m_ScreenedViewLine
[i
].Clear();
5435 void CBaseView::WrapChanged()
5437 m_nMaxLineLength
= -1;
5439 RecalcHorzScrollBar();
5442 void CBaseView::OnEditFind()
5446 m_pFindDialog
->SetFocus();
5451 if (this == m_pwndLeft
)
5453 if (this == m_pwndRight
)
5455 if (this == m_pwndBottom
)
5458 m_pFindDialog
= new CFindDlg(this);
5459 m_pFindDialog
->Create(this, id
);
5461 m_pFindDialog
->SetFindString(HasTextSelection() ? GetSelectedText() : CString());
5462 m_pFindDialog
->SetReadonly(m_bReadonly
);
5465 LRESULT
CBaseView::OnFindDialogMessage(WPARAM wParam
, LPARAM
/*lParam*/)
5467 ASSERT(m_pFindDialog
!= nullptr);
5469 if (m_pFindDialog
->IsTerminating())
5471 // invalidate the handle identifying the dialog box.
5472 m_pFindDialog
= nullptr;
5476 if(m_pFindDialog
->FindNext())
5478 //read data from dialog
5479 m_sFindText
= m_pFindDialog
->GetFindString();
5480 m_bMatchCase
= (m_pFindDialog
->MatchCase() == TRUE
);
5481 m_bLimitToDiff
= m_pFindDialog
->LimitToDiffs();
5482 m_bWholeWord
= m_pFindDialog
->WholeWord();
5485 m_sFindText
= m_sFindText
.MakeLower();
5487 BuildFindStringArray();
5488 if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Find
)
5490 if (m_pFindDialog
->SearchUp())
5495 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Count
)
5498 for (size_t i
= 0; i
< m_arFindStringLines
.size(); ++i
)
5499 count
+= m_arFindStringLines
[i
];
5501 matches
.Format(IDS_FIND_COUNT
, count
);
5502 m_pFindDialog
->SetStatusText(matches
);
5504 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Replace
)
5508 bool bFound
= false;
5509 if (m_pFindDialog
->SearchUp())
5510 bFound
= Search(SearchPrevious
, true, true, false);
5512 bFound
= Search(SearchNext
, true, true, false);
5515 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5516 CUndo::GetInstance().BeginGrouping();
5517 RemoveSelectedText();
5518 InsertText(sReplaceText
);
5519 CUndo::GetInstance().EndGrouping();
5523 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::ReplaceAll
)
5527 bool bFound
= false;
5528 int replaceCount
= 0;
5529 POINT lastPoint
= m_ptSelectionViewPosStart
;
5530 m_ptSelectionViewPosStart
.x
= m_ptSelectionViewPosStart
.y
= 0;
5531 CUndo::GetInstance().BeginGrouping();
5534 bFound
= Search(SearchNext
, true, false, true);
5537 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5538 RemoveSelectedText();
5539 InsertText(sReplaceText
);
5543 CUndo::GetInstance().EndGrouping();
5544 if (replaceCount
== 0)
5545 m_ptSelectionViewPosStart
= lastPoint
;
5547 message
.Format(IDS_FIND_REPLACED
, replaceCount
);
5548 m_pFindDialog
->SetStatusText(message
, RGB(0, 0, 0));
5555 void CBaseView::OnEditFindnextStart()
5559 if (HasTextSelection())
5561 m_sFindText
= GetSelectedText();
5562 m_bMatchCase
= false;
5563 m_bLimitToDiff
= false;
5564 m_bWholeWord
= false;
5565 m_sFindText
= m_sFindText
.MakeLower();
5567 BuildFindStringArray();
5572 m_sFindText
.Empty();
5573 BuildFindStringArray();
5577 void CBaseView::OnEditFindprevStart()
5581 if (HasTextSelection())
5583 m_sFindText
= GetSelectedText();
5584 m_bMatchCase
= false;
5585 m_bLimitToDiff
= false;
5586 m_bWholeWord
= false;
5587 m_sFindText
= m_sFindText
.MakeLower();
5589 BuildFindStringArray();
5594 m_sFindText
.Empty();
5595 BuildFindStringArray();
5599 bool CBaseView::StringFound(const CString
& str
, SearchDirection srchDir
, int& start
, int& end
) const
5604 if (srchDir
== SearchPrevious
)
5607 int laststart2
= -1;
5610 laststart2
= laststart
;
5611 laststart
= str
.Find(m_sFindText
, laststart
+ 1);
5612 } while (laststart
>= 0 && laststart
< start
);
5616 start
= str
.Find(m_sFindText
, start
);
5617 end
= start
+ m_sFindText
.GetLength();
5618 bStringFound
= (start
>= 0);
5619 if (bStringFound
&& m_bWholeWord
)
5622 bStringFound
= IsWordSeparator(str
.Mid(start
- 1, 1).GetAt(0));
5626 if (str
.GetLength() > end
)
5627 bStringFound
= IsWordSeparator(str
.Mid(end
, 1).GetAt(0));
5632 if (srchDir
== SearchPrevious
)
5638 } while (!bStringFound
&& start
>= 0);
5640 return bStringFound
;
5643 void CBaseView::OnEditFindprev()
5645 Search(SearchPrevious
, false, true, false);
5648 void CBaseView::OnEditFindnext()
5650 Search(SearchNext
, false, true, false);
5653 bool CBaseView::Search(SearchDirection srchDir
, bool useStart
, bool flashIfNotFound
, bool stopEof
)
5655 if (m_sFindText
.IsEmpty())
5660 POINT start
= useStart
? m_ptSelectionViewPosStart
: m_ptSelectionViewPosEnd
;
5662 end
.y
= m_pViewData
->GetCount()-1;
5666 if (srchDir
==SearchNext
)
5667 end
.x
= GetViewLineLength(end
.y
);
5670 end
.x
= m_ptSelectionViewPosStart
.x
;
5674 if (!HasTextSelection())
5676 start
.y
= m_ptCaretViewPos
.y
;
5677 if (srchDir
==SearchNext
)
5678 start
.x
= m_ptCaretViewPos
.x
;
5682 end
.x
= m_ptCaretViewPos
.x
;
5685 CString sSelectedText
;
5687 for (int nViewLine
=start
.y
; ;srchDir
==SearchNext
? nViewLine
++ : nViewLine
--)
5693 nViewLine
= m_pViewData
->GetCount()-1;
5694 startline
= start
.y
;
5695 if (flashIfNotFound
)
5698 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED
)), RGB(63, 127, 47));
5699 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5702 if (nViewLine
> end
.y
)
5707 startline
= start
.y
;
5708 if (flashIfNotFound
)
5711 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED
)), RGB(63, 127, 47));
5712 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5715 switch (m_pViewData
->GetState(nViewLine
))
5717 case DIFFSTATE_EMPTY
:
5719 case DIFFSTATE_UNKNOWN
:
5720 case DIFFSTATE_NORMAL
:
5721 case DIFFSTATE_FILTEREDDIFF
:
5724 case DIFFSTATE_REMOVED
:
5725 case DIFFSTATE_REMOVEDWHITESPACE
:
5726 case DIFFSTATE_ADDED
:
5727 case DIFFSTATE_ADDEDWHITESPACE
:
5728 case DIFFSTATE_WHITESPACE
:
5729 case DIFFSTATE_WHITESPACE_DIFF
:
5730 case DIFFSTATE_CONFLICTED
:
5731 case DIFFSTATE_CONFLICTED_IGNORED
:
5732 case DIFFSTATE_CONFLICTADDED
:
5733 case DIFFSTATE_CONFLICTEMPTY
:
5734 case DIFFSTATE_CONFLICTRESOLVED
:
5735 case DIFFSTATE_IDENTICALREMOVED
:
5736 case DIFFSTATE_IDENTICALADDED
:
5737 case DIFFSTATE_THEIRSREMOVED
:
5738 case DIFFSTATE_THEIRSADDED
:
5739 case DIFFSTATE_YOURSREMOVED
:
5740 case DIFFSTATE_YOURSADDED
:
5741 case DIFFSTATE_EDITED
:
5743 sSelectedText
= GetViewLineChars(nViewLine
);
5744 if (nViewLine
== start
.y
&& startline
< 0)
5745 sSelectedText
= srchDir
== SearchNext
? sSelectedText
.Mid(start
.x
) : sSelectedText
.Left(end
.x
);
5747 sSelectedText
= sSelectedText
.MakeLower();
5748 int startfound
= srchDir
== SearchNext
? 0 : sSelectedText
.GetLength();
5750 if (StringFound(sSelectedText
, srchDir
, startfound
, endfound
))
5752 HighlightViewLines(nViewLine
, nViewLine
);
5753 m_ptSelectionViewPosStart
.x
= startfound
;
5754 m_ptSelectionViewPosEnd
.x
= endfound
;
5755 if (nViewLine
== start
.y
&& startline
< 0)
5757 m_ptSelectionViewPosStart
.x
+= start
.x
;
5758 m_ptSelectionViewPosEnd
.x
+= start
.x
;
5760 m_ptSelectionViewPosEnd
.x
= m_ptSelectionViewPosStart
.x
+ m_sFindText
.GetLength();
5761 m_ptSelectionViewPosStart
.y
= nViewLine
;
5762 m_ptSelectionViewPosEnd
.y
= nViewLine
;
5763 m_ptCaretViewPos
= m_ptSelectionViewPosStart
;
5764 UpdateViewsCaretPosition();
5765 EnsureCaretVisible();
5775 if (nViewLine
== startline
)
5777 if (flashIfNotFound
)
5780 message
.Format(IDS_FIND_NOTFOUND
, static_cast<LPCWSTR
>(m_sFindText
));
5782 m_pFindDialog
->SetStatusText(message
, RGB(255, 0, 0));
5783 ::MessageBeep(0xFFFFFFFF);
5784 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 3, 100);
5790 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
5794 CString
CBaseView::GetSelectedText() const
5796 CString sSelectedText
;
5797 POINT start
= m_ptSelectionViewPosStart
;
5798 POINT end
= m_ptSelectionViewPosEnd
;
5799 if (!HasTextSelection())
5801 if (!HasSelection())
5802 return sSelectedText
;
5803 start
.y
= m_nSelViewBlockStart
;
5805 end
.y
= m_nSelViewBlockEnd
;
5806 end
.x
= GetViewLineLength(m_nSelViewBlockEnd
);
5809 return sSelectedText
;
5810 // first store the selected lines in one CString
5811 for (int nViewLine
=start
.y
; nViewLine
<=end
.y
; nViewLine
++)
5813 switch (m_pViewData
->GetState(nViewLine
))
5815 case DIFFSTATE_EMPTY
:
5817 case DIFFSTATE_UNKNOWN
:
5818 case DIFFSTATE_NORMAL
:
5819 case DIFFSTATE_REMOVED
:
5820 case DIFFSTATE_REMOVEDWHITESPACE
:
5821 case DIFFSTATE_ADDED
:
5822 case DIFFSTATE_ADDEDWHITESPACE
:
5823 case DIFFSTATE_WHITESPACE
:
5824 case DIFFSTATE_WHITESPACE_DIFF
:
5825 case DIFFSTATE_CONFLICTED
:
5826 case DIFFSTATE_CONFLICTED_IGNORED
:
5827 case DIFFSTATE_CONFLICTADDED
:
5828 case DIFFSTATE_CONFLICTEMPTY
:
5829 case DIFFSTATE_CONFLICTRESOLVED
:
5830 case DIFFSTATE_IDENTICALREMOVED
:
5831 case DIFFSTATE_IDENTICALADDED
:
5832 case DIFFSTATE_THEIRSREMOVED
:
5833 case DIFFSTATE_THEIRSADDED
:
5834 case DIFFSTATE_YOURSREMOVED
:
5835 case DIFFSTATE_YOURSADDED
:
5836 case DIFFSTATE_EDITED
:
5837 case DIFFSTATE_FILTEREDDIFF
:
5838 sSelectedText
+= GetViewLineChars(nViewLine
);
5839 sSelectedText
+= L
"\r\n";
5843 // remove the non-selected chars from the first line, last line and last \r\n
5844 int nLeftCut
= start
.x
;
5845 int nRightCut
= GetViewLineChars(end
.y
).GetLength() - end
.x
+ 2;
5846 sSelectedText
= sSelectedText
.Mid(nLeftCut
, sSelectedText
.GetLength()-nLeftCut
-nRightCut
);
5847 return sSelectedText
;
5850 void CBaseView::CheckModifications(bool& hasMods
, bool& hasConflicts
, bool& hasWhitespaceMods
, bool& hasFilteredMods
)
5853 hasConflicts
= false;
5854 hasWhitespaceMods
= false;
5855 hasFilteredMods
= false;
5859 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5861 DiffStates state
= m_pViewData
->GetState(i
);
5864 case DIFFSTATE_ADDED
:
5865 case DIFFSTATE_IDENTICALADDED
:
5866 case DIFFSTATE_THEIRSADDED
:
5867 case DIFFSTATE_YOURSADDED
:
5868 case DIFFSTATE_CONFLICTADDED
:
5869 case DIFFSTATE_IDENTICALREMOVED
:
5870 case DIFFSTATE_REMOVED
:
5871 case DIFFSTATE_THEIRSREMOVED
:
5872 case DIFFSTATE_YOURSREMOVED
:
5873 case DIFFSTATE_EMPTY
:
5876 case DIFFSTATE_CONFLICTED
:
5877 case DIFFSTATE_CONFLICTED_IGNORED
:
5878 hasConflicts
= true;
5880 case DIFFSTATE_REMOVEDWHITESPACE
:
5881 case DIFFSTATE_ADDEDWHITESPACE
:
5882 case DIFFSTATE_WHITESPACE
:
5883 case DIFFSTATE_WHITESPACE_DIFF
:
5884 hasWhitespaceMods
= true;
5886 case DIFFSTATE_FILTEREDDIFF
:
5887 hasFilteredMods
= true;
5894 void CBaseView::OnEditGotoline()
5898 // find the last and first line number
5899 int nViewLineCount
= m_pViewData
->GetCount();
5901 int nLastLineNumber
= DIFF_EMPTYLINENUMBER
;
5902 for (int nViewLine
=nViewLineCount
-1; nViewLine
>=0; --nViewLine
)
5904 nLastLineNumber
= m_pViewData
->GetLineNumber(nViewLine
);
5905 if (nLastLineNumber
!=DIFF_EMPTYLINENUMBER
)
5910 if (nLastLineNumber
==DIFF_EMPTYLINENUMBER
|| nLastLineNumber
==0) // not numbered line foud or last one is first
5915 int nFirstLineNumber
=1; // first is always 1
5918 sText
.FormatMessage(IDS_GOTOLINE
, nFirstLineNumber
, nLastLineNumber
);
5920 CGotoLineDlg
dlg(this);
5921 dlg
.SetLabel(sText
);
5922 dlg
.SetLimits(nFirstLineNumber
, nLastLineNumber
);
5923 if (dlg
.DoModal() == IDOK
)
5925 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; ++nViewLine
)
5927 if ((m_pViewData
->GetLineNumber(nViewLine
)+1) == dlg
.GetLineNumber())
5929 HighlightViewLines(nViewLine
, nViewLine
);
5936 int CBaseView::SaveFile(int nFlags
)
5939 if (m_pViewData
&& m_pWorkingFile
)
5941 CFileTextLines file
;
5942 m_SaveParams
.m_LineEndings
= m_lineendings
;
5943 m_SaveParams
.m_UnicodeType
= m_texttype
;
5944 file
.SetSaveParams(m_SaveParams
);
5946 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5948 //only copy non-removed lines
5949 DiffStates state
= m_pViewData
->GetState(i
);
5952 case DIFFSTATE_CONFLICTED
:
5953 case DIFFSTATE_CONFLICTED_IGNORED
:
5960 } while((last
<m_pViewData
->GetCount()) && ((m_pViewData
->GetState(last
)==DIFFSTATE_CONFLICTED
)||(m_pViewData
->GetState(last
)==DIFFSTATE_CONFLICTED_IGNORED
)));
5961 file
.Add(L
"<<<<<<< .mine", EOL_NOENDING
);
5962 for (int j
=first
; j
<last
; j
++)
5964 file
.Add(m_pwndRight
->m_pViewData
->GetLine(j
), m_pwndRight
->m_pViewData
->GetLineEnding(j
));
5966 file
.Add(L
"=======", EOL_NOENDING
);
5967 for (int j
=first
; j
<last
; j
++)
5969 file
.Add(m_pwndLeft
->m_pViewData
->GetLine(j
), m_pwndLeft
->m_pViewData
->GetLineEnding(j
));
5971 file
.Add(L
">>>>>>> .theirs", EOL_NOENDING
);
5975 case DIFFSTATE_EMPTY
:
5977 case DIFFSTATE_CONFLICTEMPTY
:
5978 case DIFFSTATE_IDENTICALREMOVED
:
5979 case DIFFSTATE_REMOVED
:
5980 case DIFFSTATE_THEIRSREMOVED
:
5981 case DIFFSTATE_YOURSREMOVED
:
5982 case DIFFSTATE_CONFLICTRESOLVEDEMPTY
:
5983 if ((nFlags
&SAVE_REMOVEDLINES
) == 0)
5985 // do not save removed lines
5989 file
.Add(m_pViewData
->GetLine(i
), m_pViewData
->GetLineEnding(i
));
5993 CString filename
= m_pWorkingFile
->GetFilename();
5994 if (m_pWorkingFile
->IsReadonly())
5995 if (!CCommonAppUtils::FileOpenSave(filename
, nullptr, IDS_SAVEASTITLE
, IDS_COMMONFILEFILTER
, false, m_hWnd
))
5997 if (!file
.Save(filename
))
5999 ::MessageBox(m_hWnd
, file
.GetErrorString(), L
"TortoiseGitMerge", MB_ICONERROR
);
6002 m_pWorkingFile
->SetFileName(filename
);
6003 m_pWorkingFile
->StoreFileAttributes();
6004 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
6006 CUndo::GetInstance().MarkAsOriginalState(
6008 this == m_pwndRight
,
6009 this == m_pwndBottom
);
6010 if (file
.GetCount() == 1 && file
.GetAt(0).IsEmpty() && file
.GetLineEnding(0) == EOL_NOENDING
)
6012 return file
.GetCount();
6018 int CBaseView::SaveFileTo(CString sFileName
, int nFlags
)
6022 m_pWorkingFile
->SetFileName(sFileName
);
6023 return SaveFile(nFlags
);
6029 EOL
CBaseView::GetLineEndings()
6031 return GetLineEndings(GetWhitecharsProperties().HasMixedEols
);
6034 EOL
CBaseView::GetLineEndings(bool bHasMixedEols
)
6038 return EOL_AUTOLINE
; // mixed eols - hack value
6040 if (m_lineendings
== EOL_AUTOLINE
)
6044 return m_lineendings
;
6047 void CBaseView::ReplaceLineEndings(EOL eEol
)
6049 if (eEol
== EOL_AUTOLINE
)
6054 m_lineendings
= eEol
;
6055 // replace all set EOLs
6056 // TODO store line endings and lineendings in undo
6057 //CUndo::BeginGrouping();
6058 for (int i
= 0; i
< GetViewCount(); ++i
)
6064 EOL eLineEol
= GetViewLineEnding(i
);
6065 if (eLineEol
== EOL_AUTOLINE
|| eLineEol
== EOL_NOENDING
|| eLineEol
== m_lineendings
)
6069 SetViewLineEnding(i
, eEol
);
6071 //CUndo::EndGrouping();
6072 //CUndo::saveundostep;
6077 void CBaseView::SetLineEndingStyle(EOL eEol
)
6079 m_lineendings
= eEol
;
6082 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType
)
6084 if (m_texttype
== eTextType
)
6088 m_texttype
= eTextType
;
6093 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId
)
6096 return; // nothing to be changed in read-only view
6098 dlg
.view
.LoadString(nTextId
);
6099 dlg
.texttype
= m_texttype
;
6100 dlg
.lineendings
= GetLineEndings();
6101 if (dlg
.DoModal() != IDOK
)
6103 SetTextType(dlg
.texttype
);
6104 ReplaceLineEndings(dlg
.lineendings
);
6108 Replaces lines from source view to this
6110 void CBaseView::UseViewBlock(CBaseView
* pwndView
, int nFirstViewLine
, int nLastViewLine
, std::function
<bool(int)> fnSkip
)
6112 if (!IsViewGood(pwndView
))
6116 CUndo::GetInstance().BeginGrouping();
6118 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6120 bool skip
= fnSkip(viewLine
);
6123 if (GetViewMarked(viewLine
))
6124 SetViewMarked(viewLine
, false);
6127 viewdata line
= pwndView
->GetViewData(viewLine
);
6128 if (line
.ending
!= EOL_NOENDING
)
6129 line
.ending
= m_lineendings
;
6132 case DIFFSTATE_CONFLICTEMPTY
:
6133 case DIFFSTATE_UNKNOWN
:
6134 line
.state
= DIFFSTATE_EMPTY
;
6135 case DIFFSTATE_EMPTY
:
6137 case DIFFSTATE_ADDED
:
6138 case DIFFSTATE_CONFLICTADDED
:
6139 case DIFFSTATE_CONFLICTED
:
6140 case DIFFSTATE_CONFLICTED_IGNORED
:
6141 case DIFFSTATE_IDENTICALADDED
:
6142 case DIFFSTATE_THEIRSADDED
:
6143 case DIFFSTATE_YOURSADDED
:
6144 case DIFFSTATE_IDENTICALREMOVED
:
6145 case DIFFSTATE_REMOVED
:
6146 case DIFFSTATE_THEIRSREMOVED
:
6147 case DIFFSTATE_YOURSREMOVED
:
6148 pwndView
->SetViewState(viewLine
, DIFFSTATE_NORMAL
);
6149 line
.state
= DIFFSTATE_NORMAL
;
6150 case DIFFSTATE_NORMAL
:
6155 bool marked
= GetViewMarked(viewLine
);
6156 SetViewData(viewLine
, line
);
6158 SetViewMarked(viewLine
, false);
6159 if ((m_texttype
== UnicodeType::ASCII
) && (pwndView
->GetTextType() != UnicodeType::ASCII
))
6161 // if this view is in ASCII and the other is not, we have to make sure that
6162 // the text we copy from the other view can actually be saved in ASCII encoding.
6163 // if not, we have to change this views encoding to the same encoding as the other view
6164 BOOL useDefault
= FALSE
;
6165 WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, line
.sLine
, -1, nullptr, 0, 0, &useDefault
);
6166 if (useDefault
) // a default char is required, so the char can not be saved as ASCII
6167 SetTextType(pwndView
->GetTextType());
6170 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6171 // TODO: check if copied line is same as original one set modified only when differ
6175 int nRemovedLines
= CleanEmptyLines();
6178 // make sure all non empty line have EOL set but last
6179 // wrong can be last copied line(have eol, but no line under),
6180 // or old last line (line before copied block missing eol, but have line under)
6181 // we'll check all lines to be sure
6182 int nLine
= GetViewCount();
6183 // check last line have no EOL set
6186 if (!IsViewLineEmpty(nLine
))
6188 if (GetViewLineEnding(nLine
) != EOL_NOENDING
)
6190 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6191 // so next line is empty
6192 ASSERT(IsViewLineEmpty(nLine
+1));
6193 // and we can turn it to normal empty line
6194 SetViewData(nLine
+1, viewdata(CString(), DIFFSTATE_ADDED
, 1, EOL_NOENDING
, HIDESTATE_SHOWN
));
6199 // check all (nonlast) line have EOL set
6202 if (!IsViewLineEmpty(nLine
))
6204 if (GetViewLineEnding(nLine
) == EOL_NOENDING
)
6206 SetViewLineEnding(nLine
, m_lineendings
);
6207 // in theory there should be only one line needing fix, but most of time we get over all anyway
6213 UpdateViewLineNumbers();
6216 CUndo::GetInstance().EndGrouping();
6218 if (nRemovedLines
!=0)
6220 // some lines are gone update selection
6222 SetupAllViewSelection(nFirstViewLine
, nLastViewLine
- nRemovedLines
);
6224 BuildAllScreen2ViewVector();
6225 pwndView
->Invalidate();
6229 void CBaseView::MarkBlock(bool marked
, int nFirstViewLine
, int nLastViewLine
)
6233 CUndo::GetInstance().BeginGrouping();
6235 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6236 SetViewMarked(viewLine
, marked
);
6240 CUndo::GetInstance().EndGrouping();
6242 BuildAllScreen2ViewVector();
6247 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView
*pwndView
)
6249 auto fn
= [this](int viewLine
) -> bool { return GetViewMarked(viewLine
) || GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6250 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6253 void CBaseView::UseViewFileOfMarked(CBaseView
*pwndView
)
6255 auto fn
= [this](int viewLine
) -> bool { return !GetViewMarked(viewLine
) || GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6256 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6259 void CBaseView::UseViewFileExceptEdited(CBaseView
*pwndView
)
6261 auto fn
= [this](int viewLine
) -> bool { return GetViewState(viewLine
) == DIFFSTATE_EDITED
; };
6262 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6265 int CBaseView::GetLargestSpaceStreak(const CString
& line
)
6269 for (int i
= 0; i
< line
.GetLength(); ++i
)
6275 maxstreak
= std::max(count
, maxstreak
);
6279 return std::max(count
, maxstreak
);
6282 int CBaseView::GetIndentCharsForLine(int x
, int y
)
6284 const int maxGuessLine
= 100;
6286 const CString
& line
= GetViewLine(y
);
6287 if (m_nTabMode
& TABMODE_SMARTINDENT
)
6289 // if the line contains one tab, use tabs
6290 // we can not test for spaces, since even if tabs are used,
6291 // spaces are used in a tabified file for alignment.
6292 if (line
.Find(L
'\t') >= 0)
6293 nTabMode
= 0; // use tabs
6294 else if (GetLargestSpaceStreak(line
) > m_nTabSize
)
6295 nTabMode
= 1; // use spaces
6297 // detect lines nearby
6298 for (int i
= y
- 1, j
= y
+ 1; nTabMode
== -1; --i
, ++j
)
6300 bool above
= i
>= 0 && i
>= y
- maxGuessLine
;
6301 bool below
= j
< GetViewCount() && j
<= y
+ maxGuessLine
;
6302 if (!(above
|| below
))
6304 auto ac
= CString();
6305 auto bc
= CString();
6307 ac
= GetViewLine(i
);
6309 bc
= GetViewLine(j
);
6310 if ((ac
.Find(L
'\t') >= 0) || (bc
.Find(L
'\t') >= 0))
6315 else if ((GetLargestSpaceStreak(ac
) > m_nTabSize
) && (GetLargestSpaceStreak(bc
) > m_nTabSize
))
6323 nTabMode
= m_nTabMode
& TABMODE_USESPACES
;
6328 x
= CountExpandedChars(line
, x
);
6329 return (m_nTabSize
- (x
% m_nTabSize
));
6336 void CBaseView::AddIndentationForSelectedBlock()
6338 bool bModified
= false;
6339 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6341 // skip the line if no character is selected in the last selected line
6342 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6347 if (IsLineEmpty(nViewLine
))
6351 const CString
&sLine
= GetViewLine(nViewLine
);
6352 CString sTemp
= sLine
;
6353 if (sTemp
.Trim().IsEmpty())
6355 // skip empty and whitechar only lines
6358 // add tab to line start (alternatively m_nTabSize spaces can be used)
6360 int indentChars
= GetIndentCharsForLine(0, nViewLine
);
6361 tabStr
= indentChars
> 0 ? CString(L
' ', indentChars
) : CString("\t");
6362 SetViewLine(nViewLine
, tabStr
+ sLine
);
6369 BuildAllScreen2ViewVector();
6373 void CBaseView::RemoveIndentationForSelectedBlock()
6375 bool bModified
= false;
6376 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6378 // skip the line if no character is selected in the last selected line
6379 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6384 if (IsLineEmpty(nViewLine
))
6388 CString sLine
= GetViewLine(nViewLine
);
6389 // remove up to n spaces from line start
6390 // and one tab (if less then n spaces was removed)
6392 while (nPos
<m_nTabSize
)
6394 switch (sLine
[nPos
])
6406 sLine
.Delete(0, nPos
);
6407 SetViewLine(nViewLine
, sLine
);
6415 BuildAllScreen2ViewVector();
6420 there are two possible versions
6421 - convert tabs to spaces only in front of text (implemented)
6422 - convert all tabs to spaces
6424 void CBaseView::ConvertTabToSpaces()
6426 bool bModified
= false;
6427 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6429 if (IsLineEmpty(nViewLine
))
6433 const CString
&sLine
= GetViewLine(nViewLine
);
6434 bool bTabToConvertFound
= false;
6437 while (nPosIn
<sLine
.GetLength())
6439 switch (sLine
[nPosIn
])
6447 bTabToConvertFound
= true;
6448 nPosOut
= (nPosOut
+m_nTabSize
) - nPosOut
%m_nTabSize
;
6453 if (bTabToConvertFound
)
6455 CString sLineNew
= sLine
;
6456 sLineNew
.Delete(0, nPosIn
);
6457 sLineNew
= CString(' ', nPosOut
) + sLineNew
;
6458 SetViewLine(nViewLine
, sLineNew
);
6466 BuildAllScreen2ViewVector();
6471 there are two possible version
6472 - convert spaces to tabs only in front of text (implemented)
6473 - convert all spaces to tabs
6475 void CBaseView::Tabularize()
6477 bool bModified
= false;
6478 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6480 if (IsLineEmpty(nViewLine
))
6484 const CString
&sLine
= GetViewLine(nViewLine
);
6486 int nTabCount
= 0; // total tabs to be used
6487 int nSpaceCount
= 0; // number of spaces in tab size run
6489 while (nPos
<sLine
.GetLength())
6491 switch (sLine
[nPos
++])
6495 if (++nSpaceCount
< m_nTabSize
)
6509 CString sLineNew
= sLine
;
6510 sLineNew
.Delete(0, nDel
);
6511 sLineNew
= CString('\t', nTabCount
) + sLineNew
;
6512 if (sLine
!=sLineNew
)
6514 SetViewLine(nViewLine
, sLineNew
);
6523 BuildAllScreen2ViewVector();
6527 void CBaseView::RemoveTrailWhiteChars()
6529 bool bModified
= false;
6530 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6532 if (IsLineEmpty(nViewLine
))
6536 const CString
&sLine
= GetViewLine(nViewLine
);
6537 CString sLineNew
= sLine
;
6538 sLineNew
.TrimRight();
6539 if (sLine
.GetLength()!=sLineNew
.GetLength())
6541 SetViewLine(nViewLine
, sLineNew
);
6549 BuildAllScreen2ViewVector();
6553 CBaseView::TWhitecharsProperties
CBaseView::GetWhitecharsProperties()
6555 if (GetViewCount()>10000)
6557 // 10k lines is enough to check
6558 TWhitecharsProperties oRet
= {true, true, true, true};
6561 TWhitecharsProperties oRet
= {};
6562 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6564 if (IsLineEmpty(nViewLine
))
6568 const CString
&sLine
= GetViewLine(nViewLine
);
6569 if (sLine
.IsEmpty())
6573 // check leading whites for convertible tabs and spaces
6575 int nSpaceCount
= 0; // number of spaces in tab size run
6576 while (nPos
<sLine
.GetLength() && (!oRet
.HasSpacesToConvert
|| !oRet
.HasTabsToConvert
))
6578 switch (sLine
[nPos
++])
6581 if (++nSpaceCount
>= m_nTabSize
)
6583 oRet
.HasSpacesToConvert
= true;
6587 oRet
.HasTabsToConvert
= true;
6590 oRet
.HasSpacesToConvert
= true;
6597 // check trailing whites for removable chars
6598 switch (sLine
[sLine
.GetLength()-1])
6602 oRet
.HasTrailWhiteChars
= true;
6606 EOL eLineEol
= GetViewLineEnding(nViewLine
);
6607 if (!oRet
.HasMixedEols
&& (eLineEol
!= m_lineendings
) && (eLineEol
!= EOL_AUTOLINE
) && (eLineEol
!= EOL_NOENDING
))
6609 oRet
.HasMixedEols
= true;
6615 void CBaseView::InsertText(const CString
& sText
)
6619 POINT ptCaretViewPos
= GetCaretViewPosition();
6620 int nLeft
= ptCaretViewPos
.x
;
6621 int nViewLine
= ptCaretViewPos
.y
;
6623 if ((nViewLine
== 0) && (GetViewCount() == 0))
6624 OnChar(VK_RETURN
, 0, 0);
6626 std::vector
<CString
> lines
;
6629 while ((nEolPos
= sText
.Find('\r', nEolPos
)) >= 0)
6631 CString sLine
= sText
.Mid(nStart
, nEolPos
- nStart
);
6632 lines
.push_back(sLine
);
6636 CString sLine
= sText
.Mid(nStart
);
6637 lines
.push_back(sLine
);
6639 int nLinesToPaste
= static_cast<int>(lines
.size());
6640 if (nLinesToPaste
> 1)
6644 // We want to undo the multiline insertion in a single step.
6645 CUndo::GetInstance().BeginGrouping();
6647 sLine
= GetViewLineChars(nViewLine
);
6648 CString sLineLeft
= sLine
.Left(nLeft
);
6649 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
6650 EOL eOriginalEnding
= GetViewLineEnding(nViewLine
);
6651 viewdata
newLine(L
"", DIFFSTATE_EDITED
, 1, m_lineendings
, HIDESTATE_SHOWN
);
6652 if (!lines
[0].IsEmpty() || !sLineRight
.IsEmpty() || (eOriginalEnding
!= m_lineendings
))
6654 newLine
.sLine
= sLineLeft
+ lines
[0];
6655 SetViewData(nViewLine
, newLine
);
6658 int nInsertLine
= nViewLine
;
6659 for (int i
= 1; i
< nLinesToPaste
- 1; i
++)
6661 newLine
.sLine
= lines
[i
];
6662 InsertViewData(++nInsertLine
, newLine
);
6664 newLine
.sLine
= lines
[nLinesToPaste
- 1] + sLineRight
;
6665 newLine
.ending
= eOriginalEnding
;
6666 InsertViewData(++nInsertLine
, newLine
);
6671 // adds new lines everywhere except me
6672 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!= this)
6674 m_pwndLeft
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6676 if (IsViewGood(m_pwndRight
) && m_pwndRight
!= this)
6678 m_pwndRight
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6680 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!= this)
6682 m_pwndBottom
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6686 UpdateViewLineNumbers();
6687 CUndo::GetInstance().EndGrouping();
6689 ptCaretViewPos
= SetupPoint(lines
[nLinesToPaste
- 1].GetLength(), nInsertLine
);
6693 // single line text - just insert it
6694 sLine
= GetViewLineChars(nViewLine
);
6695 sLine
.Insert(nLeft
, sText
);
6696 ptCaretViewPos
= SetupPoint(nLeft
+ sText
.GetLength(), nViewLine
);
6697 SetViewLine(nViewLine
, sLine
);
6699 auto viewState
= GetViewState(nViewLine
);
6700 if (IsStateEmpty(viewState
) || IsStateConflicted(viewState
) || viewState
== DIFFSTATE_IDENTICALREMOVED
)
6702 // if not last line set EOL
6703 for (int nCheckViewLine
= nViewLine
+ 1; nCheckViewLine
< GetViewCount(); ++nCheckViewLine
)
6705 if (!IsViewLineEmpty(nCheckViewLine
))
6707 SetViewLineEnding(nViewLine
, m_lineendings
);
6711 // make sure previous (non empty) line have EOL set
6712 for (int nCheckViewLine
= nViewLine
- 1; nCheckViewLine
> 0; --nCheckViewLine
)
6714 if (!IsViewLineEmpty(nCheckViewLine
))
6716 if (GetViewLineEnding(nCheckViewLine
) == EOL_NOENDING
)
6717 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
6723 SetViewState(nViewLine
, DIFFSTATE_EDITED
);
6729 BuildAllScreen2ViewVector();
6730 UpdateCaretViewPosition(ptCaretViewPos
);
6733 ULONG
CBaseView::GetGestureStatus(CPoint
/*ptTouch*/)