1
// TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2021 - TortoiseSVN
4 // Copyright (C) 2011-2012, 2017-2024 TortoiseGit
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(GetSafeHwnd(), 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()
74 m_bViewWhitespace
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
75 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
76 m_bShowInlineDiff
= CRegDWORD(L
"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE
);
77 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
78 m_InlineAddedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\InlineAdded", INLINEADDED_COLOR
);
79 m_InlineRemovedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\InlineRemoved", INLINEREMOVED_COLOR
);
80 m_ModifiedBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\ColorModifiedB", MODIFIED_COLOR
);
81 m_InlineAddedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkInlineAdded", INLINEADDED_DARK_COLOR
);
82 m_InlineRemovedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkInlineRemoved", INLINEREMOVED_DARK_COLOR
);
83 m_ModifiedDarkBk
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\DarkColorModifiedB", MODIFIED_DARK_COLOR
);
84 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\Whitespace", CTheme::Instance().GetThemeColor(GetSysColor(COLOR_3DSHADOW
)));
85 m_sWordSeparators
= CRegString(L
"Software\\TortoiseGitMerge\\WordSeparators", L
"[]();:.,{}!@#$%^&*-+=|/\\<>'`~\"?");
86 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
87 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
88 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
89 m_bEditorConfigEnabled
= !!static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\EnableEditorConfig", FALSE
));
90 std::fill_n(m_apFonts
, fontsCount
, static_cast<CFont
*>(nullptr));
92 int cxIcon
= GetSystemMetrics(SM_CXSMICON
);
93 int cyIcon
= GetSystemMetrics(SM_CYSMICON
);
94 m_hConflictedIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDLINE
, cxIcon
, cyIcon
);
95 m_hConflictedIgnoredIcon
= CCommonAppUtils::LoadIconEx(IDI_CONFLICTEDIGNOREDLINE
, cxIcon
, cyIcon
);
96 m_hRemovedIcon
= CCommonAppUtils::LoadIconEx(IDI_REMOVEDLINE
, cxIcon
, cyIcon
);
97 m_hAddedIcon
= CCommonAppUtils::LoadIconEx(IDI_ADDEDLINE
, cxIcon
, cyIcon
);
98 m_hWhitespaceBlockIcon
= CCommonAppUtils::LoadIconEx(IDI_WHITESPACELINE
, cxIcon
, cyIcon
);
99 m_hEqualIcon
= CCommonAppUtils::LoadIconEx(IDI_EQUALLINE
, cxIcon
, cyIcon
);
100 m_hLineEndingCR
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCR
, cxIcon
, cyIcon
);
101 m_hLineEndingCRLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGCRLF
, cxIcon
, cyIcon
);
102 m_hLineEndingLF
= CCommonAppUtils::LoadIconEx(IDI_LINEENDINGLF
, cxIcon
, cyIcon
);
103 m_hEditedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEEDITED
, cxIcon
, cyIcon
);
104 m_hMovedIcon
= CCommonAppUtils::LoadIconEx(IDI_MOVEDLINE
, cxIcon
, cyIcon
);
105 m_hMarkedIcon
= CCommonAppUtils::LoadIconEx(IDI_LINEMARKED
, cxIcon
, cyIcon
);
106 m_margincursor
= static_cast<HCURSOR
>(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR
), IMAGE_CURSOR
, 0, 0, LR_DEFAULTSIZE
));
108 for (int i
=0; i
<1024; ++i
)
109 m_sConflictedText
+= L
"??";
110 m_sNoLineNr
.LoadString(IDS_EMPTYLINETT
);
116 m_Eols
[static_cast<int>(EOL::LF
)] = L
"\n"; // x0a
117 m_Eols
[static_cast<int>(EOL::CR
)] = L
"\r"; // x0d
118 m_Eols
[static_cast<int>(EOL::CRLF
)] = L
"\r\n"; // x0d x0a
119 m_Eols
[static_cast<int>(EOL::LFCR
)] = L
"\n\r";
120 m_Eols
[static_cast<int>(EOL::VT
)] = L
"\v"; // x0b
121 m_Eols
[static_cast<int>(EOL::FF
)] = L
"\f"; // x0c
122 m_Eols
[static_cast<int>(EOL::NEL
)] = L
"\x85";
123 m_Eols
[static_cast<int>(EOL::LS
)] = L
"\x2028";
124 m_Eols
[static_cast<int>(EOL::PS
)] = L
"\x2029";
125 m_Eols
[static_cast<int>(EOL::AutoLine
)] = m_Eols
[static_cast<int>(m_lineendings
== EOL::AutoLine
? EOL::CRLF
: m_lineendings
)];
126 m_SaveParams
.m_LineEndings
= EOL::AutoLine
;
127 m_SaveParams
.m_UnicodeType
= CFileTextLines::UnicodeType::AUTOTYPE
;
129 m_themeCallbackId
= CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme()); });
132 CBaseView::~CBaseView()
134 CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId
);
137 DestroyCursor(m_margincursor
);
140 BEGIN_MESSAGE_MAP(CBaseView
, CView
)
153 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE
, OnMergeNextdifference
)
154 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE
, OnMergePreviousdifference
)
155 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
156 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
159 ON_COMMAND(ID_EDIT_COPY
, OnEditCopy
)
161 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT
, OnMergePreviousconflict
)
162 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT
, OnMergeNextconflict
)
164 ON_COMMAND(ID_CARET_DOWN
, &CBaseView::OnCaretDown
)
165 ON_COMMAND(ID_CARET_LEFT
, &CBaseView::OnCaretLeft
)
166 ON_COMMAND(ID_CARET_RIGHT
, &CBaseView::OnCaretRight
)
167 ON_COMMAND(ID_CARET_UP
, &CBaseView::OnCaretUp
)
168 ON_COMMAND(ID_CARET_WORDLEFT
, &CBaseView::OnCaretWordleft
)
169 ON_COMMAND(ID_CARET_WORDRIGHT
, &CBaseView::OnCaretWordright
)
170 ON_COMMAND(ID_EDIT_CUT
, &CBaseView::OnEditCut
)
171 ON_COMMAND(ID_EDIT_PASTE
, &CBaseView::OnEditPaste
)
173 ON_WM_LBUTTONDBLCLK()
174 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF
, &CBaseView::OnNavigateNextinlinediff
)
175 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF
, &CBaseView::OnNavigatePrevinlinediff
)
176 ON_COMMAND(ID_EDIT_SELECTALL
, &CBaseView::OnEditSelectall
)
177 ON_COMMAND(ID_EDIT_FIND
, OnEditFind
)
178 ON_REGISTERED_MESSAGE(m_FindDialogMessage
, OnFindDialogMessage
)
179 ON_COMMAND(ID_EDIT_FINDNEXT
, OnEditFindnext
)
180 ON_COMMAND(ID_EDIT_FINDPREV
, OnEditFindprev
)
181 ON_COMMAND(ID_EDIT_FINDNEXTSTART
, OnEditFindnextStart
)
182 ON_COMMAND(ID_EDIT_FINDPREVSTART
, OnEditFindprevStart
)
183 ON_COMMAND(ID_EDIT_GOTOLINE
, &CBaseView::OnEditGotoline
)
188 void CBaseView::DocumentUpdated()
194 m_nLastScreenChars
= -1;
195 m_nMaxLineLength
= -1;
199 m_bOtherDiffChecked
= false;
203 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
204 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
205 m_bViewLinenumbers
= CRegDWORD(L
"Software\\TortoiseGitMerge\\ViewLinenumbers", 1);
206 m_bIconLFs
= CRegDWORD(L
"Software\\TortoiseGitMerge\\IconLFs", 0);
207 m_nInlineDiffMaxLineLength
= CRegDWORD(L
"Software\\TortoiseGitMerge\\InlineDiffMaxLineLength", 3000);
208 m_Eols
[static_cast<int>(EOL::AutoLine
)] = m_Eols
[static_cast<int>(m_lineendings
== EOL::AutoLine
? EOL::CRLF
: m_lineendings
)];
209 SetEditorConfigEnabled(m_bEditorConfigEnabled
);
211 ClearCurrentSelection();
213 SetTheme(CTheme::Instance().IsDarkTheme());
217 void CBaseView::SetEditorConfigEnabled(bool bEditorConfigEnabled
)
219 m_bEditorConfigEnabled
= bEditorConfigEnabled
;
220 m_nTabSize
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabSize", 4));
221 m_nTabMode
= static_cast<int>(CRegDWORD(L
"Software\\TortoiseGitMerge\\TabMode", TABMODE_NONE
));
222 if (m_bEditorConfigEnabled
)
224 m_bEditorConfigLoaded
= FALSE
; // no editorconfig entries loaded
225 CEditorConfigWrapper ec
;
226 if (ec
.Load(m_sReflectedName
.IsEmpty() ? m_sFullFilePath
: m_sReflectedName
))
228 m_bEditorConfigLoaded
= TRUE
;
229 if (ec
.m_nTabWidth
!= nullptr)
230 m_nTabSize
= ec
.m_nTabWidth
;
231 if (ec
.m_bIndentStyle
!= nullptr)
232 m_nTabMode
= (m_nTabMode
& ~TABMODE_USESPACES
) | (ec
.m_bIndentStyle
? TABMODE_USESPACES
: TABMODE_NONE
);
237 void CBaseView::DPIChanged()
243 m_nLastScreenChars
= -1;
244 m_nMaxLineLength
= -1;
249 SetTheme(CTheme::Instance().IsDarkTheme());
250 EnsureCaretVisible();
254 static CString
GetTabModeString(int nTabMode
, int nTabSize
, bool bEditorConfig
)
257 if (nTabMode
& TABMODE_USESPACES
)
261 text
.AppendFormat(L
" %d", nTabSize
);
262 if (nTabMode
& TABMODE_SMARTINDENT
)
269 void CBaseView::UpdateStatusBar()
271 int nRemovedLines
= 0;
273 int nConflictedLines
= 0;
277 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
279 DiffState state
= m_pViewData
->GetState(i
);
282 case DiffState::Added
:
283 case DiffState::IdenticalAdded
:
284 case DiffState::TheirsAdded
:
285 case DiffState::YoursAdded
:
286 case DiffState::ConflictAdded
:
289 case DiffState::IdenticalRemoved
:
290 case DiffState::Removed
:
291 case DiffState::TheirsRemoved
:
292 case DiffState::YoursRemoved
:
295 case DiffState::Conflicted
:
296 case DiffState::Conflicted_Ignored
:
308 sBarText
+= CFileTextLines::GetEncodingName(m_texttype
);
309 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
310 sBarText
+= GetEolName(m_lineendings
);
311 sBarText
+= sBarText
.IsEmpty() ? L
"" : L
" ";
313 if (sBarText
.IsEmpty())
319 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
320 if (!sBarText
.IsEmpty())
326 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
327 if (!sBarText
.IsEmpty())
331 if (nConflictedLines
)
333 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
334 if (!sBarText
.IsEmpty())
338 if (m_pwndStatusBar
|| m_pwndRibbonStatusBar
)
345 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
347 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
349 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
351 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
352 sBarText
= sTemp
+sBarText
;
354 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
356 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
357 sBarText
= sTemp
+sBarText
;
359 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
360 m_pwndStatusBar
->GetPaneInfo(nIndex
, nID
, nStyle
, cxWidth
);
361 //calculate the width of the text
362 CDC
* pDC
= m_pwndStatusBar
->GetDC();
365 CSize size
= pDC
->GetTextExtent(sBarText
);
366 m_pwndStatusBar
->SetPaneInfo(nIndex
, nID
, nStyle
, size
.cx
+2);
369 m_pwndStatusBar
->SetPaneText(nIndex
, sBarText
);
371 else if (m_pwndRibbonStatusBar
)
373 if (!IsViewGood(m_pwndBottom
))
374 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
375 if ((m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
) && (IsViewGood(this)))
377 m_pwndRibbonStatusBar
->RemoveElement(ID_INDICATOR_BOTTOMVIEW
);
378 auto apBtnGroupBottom
= std::make_unique
<CMFCRibbonButtonsGroup
>();
379 apBtnGroupBottom
->SetID(ID_INDICATOR_BOTTOMVIEW
);
380 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR
, CString(MAKEINTRESOURCE(IDS_STATUSBAR_BOTTOMVIEW
)), TRUE
));
381 CMFCRibbonButton
* pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING
, L
"");
382 m_pMainFrame
->FillEncodingButton(pButton
, ID_INDICATOR_BOTTOMENCODINGSTART
);
383 apBtnGroupBottom
->AddButton(pButton
);
384 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOEOL
, L
"");
385 m_pMainFrame
->FillEOLButton(pButton
, ID_INDICATOR_BOTTOMEOLSTART
);
386 apBtnGroupBottom
->AddButton(pButton
);
387 pButton
= new CMFCRibbonButton(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE
, L
"");
388 m_pMainFrame
->FillTabModeButton(pButton
, ID_INDICATOR_BOTTOMTABMODESTART
);
389 apBtnGroupBottom
->AddButton(pButton
);
390 apBtnGroupBottom
->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_BOTTOMVIEW
, L
"", TRUE
));
391 m_pwndRibbonStatusBar
->AddExtendedElement(apBtnGroupBottom
.release(), L
"");
394 CMFCRibbonButtonsGroup
* pGroup
= DYNAMIC_DOWNCAST(CMFCRibbonButtonsGroup
, m_pwndRibbonStatusBar
->FindByID(m_nStatusBarID
));
397 CMFCRibbonStatusBarPane
* pPane
= DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane
, pGroup
->GetButton(4));
400 pPane
->SetText(sBarText
);
402 CMFCRibbonButton
* pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(1));
405 pButton
->SetText(CFileTextLines::GetEncodingName(m_texttype
));
406 pButton
->SetDescription(CFileTextLines::GetEncodingName(m_texttype
));
408 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(2));
411 pButton
->SetText(GetEolName(m_lineendings
));
412 pButton
->SetDescription(GetEolName(m_lineendings
));
414 pButton
= DYNAMIC_DOWNCAST(CMFCRibbonButton
, pGroup
->GetButton(3));
417 pButton
->SetText(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
418 pButton
->SetDescription(GetTabModeString(m_nTabMode
, m_nTabSize
, m_bEditorConfigEnabled
&& m_bEditorConfigLoaded
));
421 if (m_pwndRibbonStatusBar
->GetSafeHwnd())
423 m_pwndRibbonStatusBar
->RecalcLayout();
424 m_pwndRibbonStatusBar
->Invalidate();
430 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
432 if (!CView::PreCreateWindow(cs
))
435 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
436 cs
.style
&= ~WS_BORDER
;
437 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
438 ::LoadCursor(nullptr, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+ 1), nullptr);
440 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
441 if (!pParentWnd
|| !pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
443 // View must always create its own scrollbars,
444 // if only it's not used within splitter
445 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
447 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
451 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
458 if (!m_apFonts
[nIndex
])
460 m_apFonts
[nIndex
] = new CFont
;
461 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
462 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
463 m_lfBaseFont
.lfItalic
= static_cast<BYTE
>(bItalic
);
464 m_lfBaseFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(*this, static_cast<DWORD
>(CRegDWORD(L
"Software\\TortoiseGitMerge\\LogFontSize", 10)));
465 wcsncpy_s(m_lfBaseFont
.lfFaceName
, static_cast<LPCWSTR
>(static_cast<CString
>(CRegString(L
"Software\\TortoiseGitMerge\\LogFontName", L
"Consolas"))), _countof(m_lfBaseFont
.lfFaceName
) - 1);
466 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
468 delete m_apFonts
[nIndex
];
469 m_apFonts
[nIndex
] = nullptr;
470 return CView::GetFont();
473 return m_apFonts
[nIndex
];
476 void CBaseView::CalcLineCharDim()
481 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
482 const CSize szCharExt
= pDC
->GetTextExtent(L
"X");
483 pDC
->SelectObject(pOldFont
);
486 m_nLineHeight
= szCharExt
.cy
;
487 if (m_nLineHeight
<= 0)
489 m_nCharWidth
= szCharExt
.cx
;
490 if (m_nCharWidth
<= 0)
494 int CBaseView::GetScreenChars()
496 if (m_nScreenChars
== -1)
499 GetClientRect(&rect
);
500 m_nScreenChars
= (rect
.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL
)) / GetCharWidth();
501 if (m_nScreenChars
< 0)
504 return m_nScreenChars
;
507 int CBaseView::GetAllMinScreenChars() const
509 int nChars
= INT_MAX
;
510 if (IsLeftViewGood())
511 nChars
= std::min
<int>(nChars
, m_pwndLeft
->GetScreenChars());
512 if (IsRightViewGood())
513 nChars
= std::min
<int>(nChars
, m_pwndRight
->GetScreenChars());
514 if (IsBottomViewGood())
515 nChars
= std::min
<int>(nChars
, m_pwndBottom
->GetScreenChars());
516 return (nChars
==INT_MAX
) ? 0 : nChars
;
519 int CBaseView::GetAllMaxLineLength() const
522 if (IsLeftViewGood())
523 nLength
= std::max
<int>(nLength
, m_pwndLeft
->GetMaxLineLength());
524 if (IsRightViewGood())
525 nLength
= std::max
<int>(nLength
, m_pwndRight
->GetMaxLineLength());
526 if (IsBottomViewGood())
527 nLength
= std::max
<int>(nLength
, m_pwndBottom
->GetMaxLineLength());
531 int CBaseView::GetLineHeight()
533 if (m_nLineHeight
== -1)
535 if (m_nLineHeight
<= 0)
537 return m_nLineHeight
;
540 int CBaseView::GetCharWidth()
542 if (m_nCharWidth
== -1)
544 if (m_nCharWidth
<= 0)
549 int CBaseView::GetMaxLineLength()
551 if (m_nMaxLineLength
== -1)
553 m_nMaxLineLength
= 0;
554 int nLineCount
= GetLineCount();
557 m_nMaxLineLength
= GetLineLengthWithTabsConverted(0);
558 return m_nMaxLineLength
;
560 for (int i
=0; i
<nLineCount
; i
++)
562 int nActualLength
= GetLineLengthWithTabsConverted(i
);
563 if (m_nMaxLineLength
< nActualLength
)
564 m_nMaxLineLength
= nActualLength
;
567 return m_nMaxLineLength
;
570 int CBaseView::GetLineLengthWithTabsConverted(int index
)
574 if (m_pViewData
->GetCount() == 0)
576 if (m_Screen2View
.size() <= index
)
579 if (m_pMainFrame
->m_bWrapLines
)
580 sLine
= GetLineChars(index
);
583 int viewLine
= GetViewLineForScreen(index
);
584 sLine
= m_pViewData
->GetLine(viewLine
);
587 const wchar_t* pChar
= sLine
;
588 auto nLineLength
= sLine
.GetLength();
589 for (int i
= 0; i
< nLineLength
; ++i
)
595 // GetTabSize() - 1 because the tabs are already counted
596 nLineLength
= nLineLength
+ (tabCount
* (GetTabSize() - 1));
597 ASSERT(nLineLength
>= 0);
601 int CBaseView::GetLineLength(int index
)
605 if (m_pViewData
->GetCount() == 0)
607 if (m_Screen2View
.size() <= index
)
609 int viewLine
= GetViewLineForScreen(index
);
610 if (m_pMainFrame
->m_bWrapLines
)
612 int nLineLength
= GetLineChars(index
).GetLength();
613 ASSERT(nLineLength
>= 0);
616 int nLineLength
= m_pViewData
->GetLine(viewLine
).GetLength();
617 ASSERT(nLineLength
>= 0);
621 int CBaseView::GetViewLineLength(int nViewLine
) const
625 if (m_pViewData
->GetCount() <= nViewLine
)
627 int nLineLength
= m_pViewData
->GetLine(nViewLine
).GetLength();
628 ASSERT(nLineLength
>= 0);
632 int CBaseView::GetLineCount() const
636 int nLineCount
= m_Screen2View
.size();
637 ASSERT(nLineCount
>= 0);
641 int CBaseView::GetSubLineOffset(int index
)
643 return m_Screen2View
.GetSubLineOffset(index
);
646 CString
CBaseView::GetViewLineChars(int nViewLine
) const
650 if (m_pViewData
->GetCount() <= nViewLine
)
652 return m_pViewData
->GetLine(nViewLine
);
655 CString
CBaseView::GetLineChars(int index
)
659 if (m_pViewData
->GetCount() == 0)
661 if (m_Screen2View
.size() <= index
)
663 int viewLine
= GetViewLineForScreen(index
);
664 if (m_pMainFrame
->m_bWrapLines
)
666 int subLine
= GetSubLineOffset(index
);
669 if (subLine
< CountMultiLines(viewLine
))
671 return m_ScreenedViewLine
[viewLine
].SubLines
[subLine
];
676 return m_pViewData
->GetLine(viewLine
);
679 void CBaseView::CheckOtherView()
681 if (m_bOtherDiffChecked
)
683 // find out what the 'other' file is
684 m_pOtherViewData
= nullptr;
685 m_pOtherView
= nullptr;
686 if (this == m_pwndLeft
&& IsRightViewGood())
688 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
689 m_pOtherView
= m_pwndRight
;
692 if (this == m_pwndRight
&& IsLeftViewGood())
694 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
695 m_pOtherView
= m_pwndLeft
;
698 m_bOtherDiffChecked
= true;
702 void CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
, int & nStartBlock
, int & nEndBlock
)
704 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
707 DiffState origstate
= viewData
->GetState(nLineIndex
);
709 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
710 nStartBlock
= nLineIndex
;
711 nEndBlock
= nLineIndex
;
712 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
714 DiffState state
= viewData
->GetState(nStartBlock
- 1);
715 if ((origstate
== DiffState::Empty
) && (state
!= DiffState::Normal
))
717 if ((origstate
== state
) || (state
== DiffState::Empty
))
722 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
724 DiffState state
= viewData
->GetState(nEndBlock
+ 1);
725 if ((origstate
== DiffState::Empty
) && (state
!= DiffState::Normal
))
727 if ((origstate
== state
) || (state
== DiffState::Empty
))
734 CString
CBaseView::GetWhitespaceString(CViewData
*viewData
, int nStartBlock
, int nEndBlock
)
736 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
739 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
740 len
+= viewData
->GetLine(i
).GetLength()+2;
743 // do not check for whitespace blocks if the line is too long, because
744 // reserving a lot of memory here takes too much time (performance hog)
745 if (len
> MAX_WHITESPACEBLOCK_SIZE
*256)
747 block
.Preallocate(len
+1);
748 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
750 block
+= viewData
->GetLine(i
);
751 block
+= m_Eols
[static_cast<int>(viewData
->GetLineEnding(i
))];
756 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
, int& blockstart
, int& blockend
)
762 if (!m_pOtherViewData
)
764 int viewLine
= GetViewLineForScreen(nLineIndex
);
766 (m_pViewData
->GetState(viewLine
) == DiffState::Normal
) &&
767 (m_pOtherViewData
->GetLine(viewLine
) == m_pViewData
->GetLine(viewLine
))
773 // first check whether the line itself only has whitespace changes
774 CString mine
= m_pViewData
->GetLine(viewLine
);
775 CString other
= m_pOtherViewData
->GetLine(min(viewLine
, m_pOtherViewData
->GetCount() - 1));
776 if (mine
.IsEmpty() && other
.IsEmpty())
787 FilterWhitespaces(mine
, other
);
791 int nStartBlock2
, nEndBlock2
;
792 GetWhitespaceBlock(m_pViewData
, viewLine
, blockstart
, blockend
);
793 GetWhitespaceBlock(m_pOtherViewData
, min(viewLine
, m_pOtherViewData
->GetCount() - 1), nStartBlock2
, nEndBlock2
);
794 mine
= GetWhitespaceString(m_pViewData
, blockstart
, blockend
);
799 other
= GetWhitespaceString(m_pOtherViewData
, nStartBlock2
, nEndBlock2
);
800 bIdentical
= mine
== other
;
801 FilterWhitespaces(mine
, other
);
804 return (!mine
.IsEmpty()) && (mine
== other
);
807 bool CBaseView::IsViewLineHidden(int nViewLine
)
809 return IsViewLineHidden(m_pViewData
, nViewLine
);
812 bool CBaseView::IsViewLineHidden(CViewData
* pViewData
, int nViewLine
)
814 return m_pMainFrame
->m_bCollapsed
&& (pViewData
->GetHideState(nViewLine
)!=HideState::Shown
);
817 int CBaseView::GetLineNumber(int index
) const
821 int viewLine
= GetViewLineForScreen(index
);
822 if (m_pViewData
->GetLineNumber(viewLine
)==DIFF_EMPTYLINENUMBER
)
824 return m_pViewData
->GetLineNumber(viewLine
);
827 int CBaseView::GetScreenLines()
829 if (m_nScreenLines
== -1)
832 GetClientRect(&rect
);
833 SCROLLBARINFO sbi
= { sizeof(sbi
) };
834 if (GetScrollBarInfo(OBJID_HSCROLL
, &sbi
))
836 // only use the scroll bar size if the info is correct and the scrollbar is visible
837 // if anything isn't proper, assume the scrollbar has a size of zero
838 // and calculate the screen lines without it.
839 if (!(sbi
.rgstate
[0] & STATE_SYSTEM_INVISIBLE
) && !(sbi
.rgstate
[0] & STATE_SYSTEM_UNAVAILABLE
))
841 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
842 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
843 if (m_nScreenLines
< 0)
845 return m_nScreenLines
;
848 // if the scroll bar is not visible, unavailable or there was an error,
849 // assume the scroll bar height is zero and return the screen lines here.
850 // Of course, that means the cache (using the member variable) won't work
851 // and we fetch the scroll bar info every time. But in this case the overhead is necessary.
852 return (rect
.Height() - HEADERHEIGHT
) / GetLineHeight();
854 return m_nScreenLines
;
857 int CBaseView::GetAllMinScreenLines() const
859 int nLines
= INT_MAX
;
860 if (IsLeftViewGood())
861 nLines
= m_pwndLeft
->GetScreenLines();
862 if (IsRightViewGood())
863 nLines
= std::min
<int>(nLines
, m_pwndRight
->GetScreenLines());
864 if (IsBottomViewGood())
865 nLines
= std::min
<int>(nLines
, m_pwndBottom
->GetScreenLines());
866 return (nLines
== INT_MAX
) || (nLines
< 0) ? 0 : nLines
;
869 int CBaseView::GetAllLineCount() const
872 if (IsLeftViewGood())
873 nLines
= m_pwndLeft
->GetLineCount();
874 if (IsRightViewGood())
875 nLines
= std::max
<int>(nLines
, m_pwndRight
->GetLineCount());
876 if (IsBottomViewGood())
877 nLines
= std::max
<int>(nLines
, m_pwndBottom
->GetLineCount());
881 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
883 if (IsLeftViewGood())
884 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
885 if (IsRightViewGood())
886 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
887 if (IsBottomViewGood())
888 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
891 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
894 si
.cbSize
= sizeof(si
);
898 si
.nPos
= m_nTopLine
;
902 EnableScrollBarCtrl(SB_VERT
, TRUE
);
903 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
908 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
910 si
.nMax
= GetAllLineCount();
911 si
.nPage
= GetAllMinScreenLines();
912 si
.nPos
= m_nTopLine
;
914 VERIFY(SetScrollInfo(SB_VERT
, &si
));
917 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
919 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
921 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
923 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
925 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
927 m_pwndLocator
->Invalidate();
930 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
932 // Note we cannot use nPos because of its 16-bit nature
934 si
.cbSize
= sizeof(si
);
936 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
938 int nPageLines
= GetScreenLines();
939 int nLineCount
= GetLineCount();
943 static LONG textwidth
= 0;
944 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
951 nNewTopLine
= nLineCount
- nPageLines
+ 1;
954 nNewTopLine
= m_nTopLine
- 1;
957 nNewTopLine
= m_nTopLine
+ 1;
960 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
963 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
965 case SB_THUMBPOSITION
:
966 m_ScrollTool
.Clear();
967 nNewTopLine
= si
.nTrackPos
;
971 nNewTopLine
= si
.nTrackPos
;
972 if (GetFocus() == this)
975 GetClientRect(&thumbrect
);
976 ClientToScreen(&thumbrect
);
979 thumbpoint
.x
= thumbrect
.right
;
980 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
981 m_ScrollTool
.Init(&thumbpoint
);
984 CString sTemp
= sFormat
;
985 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
986 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
988 thumbpoint
.x
-= textwidth
;
989 int line
= GetLineNumber(nNewTopLine
);
991 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
993 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
1000 if (nNewTopLine
< 0)
1002 if (nNewTopLine
>= nLineCount
)
1003 nNewTopLine
= nLineCount
- 1;
1004 ScrollToLine(nNewTopLine
);
1007 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
1009 if (IsLeftViewGood())
1010 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
1011 if (IsRightViewGood())
1012 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
1013 if (IsBottomViewGood())
1014 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
1017 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
1020 si
.cbSize
= sizeof(si
);
1024 si
.nPos
= m_nOffsetChar
;
1028 EnableScrollBarCtrl(SB_HORZ
, !m_pMainFrame
->m_bWrapLines
);
1029 if (!m_pMainFrame
->m_bWrapLines
)
1031 int minScreenChars
= GetAllMinScreenChars();
1032 int maxLineLength
= GetAllMaxLineLength();
1033 if (minScreenChars
>= maxLineLength
&& m_nOffsetChar
> 0)
1038 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
1040 si
.nMax
= m_pMainFrame
->m_bWrapLines
? minScreenChars
: maxLineLength
;
1041 si
.nMax
+= GetMarginWidth()/GetCharWidth();
1042 si
.nPage
= GetScreenChars();
1043 si
.nPos
= m_nOffsetChar
;
1046 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
1049 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
1051 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
1053 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1055 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1057 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
1059 m_pwndLocator
->Invalidate();
1062 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
1065 si
.cbSize
= sizeof(si
);
1067 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
1069 int nPageChars
= GetScreenChars();
1070 int nMaxLineLength
= GetMaxLineLength();
1079 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
1082 nNewOffset
= m_nOffsetChar
- 1;
1085 nNewOffset
= m_nOffsetChar
+ 1;
1088 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
1091 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
1093 case SB_THUMBPOSITION
:
1095 nNewOffset
= si
.nTrackPos
;
1101 if (nNewOffset
>= nMaxLineLength
)
1102 nNewOffset
= nMaxLineLength
- 1;
1105 ScrollToChar(nNewOffset
, TRUE
);
1108 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
1110 if (m_nOffsetChar
!= nNewOffsetChar
)
1112 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
1113 m_nOffsetChar
= nNewOffsetChar
;
1115 GetClientRect(&rcScroll
);
1116 rcScroll
.left
+= GetMarginWidth();
1117 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1118 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
1119 // update the view header
1122 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1123 InvalidateRect(&rcScroll
, FALSE
);
1125 if (bTrackScrollBar
)
1126 RecalcHorzScrollBar(TRUE
);
1128 if (m_pwndLineDiffBar
)
1129 m_pwndLineDiffBar
->Invalidate();
1133 void CBaseView::ScrollAllToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/* = TRUE */)
1136 m_pwndLeft
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1138 m_pwndRight
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1140 m_pwndBottom
->ScrollToChar(nNewOffsetChar
, bTrackScrollBar
);
1143 void CBaseView::ScrollAllSide(int delta
)
1145 int nNewOffset
= m_nOffsetChar
;
1146 nNewOffset
+= delta
;
1147 int nMaxLineLength
= GetMaxLineLength();
1148 if (nNewOffset
>= nMaxLineLength
)
1149 nNewOffset
= nMaxLineLength
- 1;
1152 ScrollAllToChar(nNewOffset
, TRUE
);
1153 if (m_pwndLineDiffBar
)
1154 m_pwndLineDiffBar
->Invalidate();
1158 void CBaseView::ScrollSide(int delta
)
1160 int nNewOffset
= m_nOffsetChar
;
1161 nNewOffset
+= delta
;
1162 int nMaxLineLength
= GetMaxLineLength();
1163 if (nNewOffset
>= nMaxLineLength
)
1164 nNewOffset
= nMaxLineLength
- 1;
1167 ScrollToChar(nNewOffset
, TRUE
);
1168 if (m_pwndLineDiffBar
)
1169 m_pwndLineDiffBar
->Invalidate();
1173 void CBaseView::ScrollVertical(short zDelta
)
1175 const int nLineCount
= GetLineCount();
1176 int nTopLine
= m_nTopLine
;
1177 nTopLine
-= (zDelta
/30);
1180 if (nTopLine
>= nLineCount
)
1181 nTopLine
= nLineCount
- 1;
1182 ScrollToLine(nTopLine
, TRUE
);
1185 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
1187 if (m_nTopLine
!= nNewTopLine
)
1189 if (nNewTopLine
< 0)
1192 int nScrollLines
= m_nTopLine
- nNewTopLine
;
1194 m_nTopLine
= nNewTopLine
;
1196 GetClientRect(&rcScroll
);
1197 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
1198 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
1200 if (bTrackScrollBar
)
1201 RecalcVertScrollBar(TRUE
);
1207 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
1209 pdc
->FillSolidRect(rect
, CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
)));
1211 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1213 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1214 HICON icon
= nullptr;
1215 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
1216 TScreenedViewLine::EIcon eIcon
= m_ScreenedViewLine
[nViewLine
].eIcon
;
1217 if (eIcon
==TScreenedViewLine::ICN_UNKNOWN
)
1219 DiffState state
= m_pViewData
->GetState(nViewLine
);
1222 case DiffState::Added
:
1223 case DiffState::TheirsAdded
:
1224 case DiffState::YoursAdded
:
1225 case DiffState::IdenticalAdded
:
1226 case DiffState::ConflictAdded
:
1227 eIcon
= TScreenedViewLine::ICN_ADD
;
1229 case DiffState::Removed
:
1230 case DiffState::TheirsRemoved
:
1231 case DiffState::YoursRemoved
:
1232 case DiffState::IdenticalRemoved
:
1233 eIcon
= TScreenedViewLine::ICN_REMOVED
;
1235 case DiffState::Conflicted
:
1236 eIcon
= TScreenedViewLine::ICN_CONFLICT
;
1238 case DiffState::Conflicted_Ignored
:
1239 eIcon
= TScreenedViewLine::ICN_CONFLICTIGNORED
;
1241 case DiffState::Edited
:
1242 eIcon
= TScreenedViewLine::ICN_EDIT
;
1247 bool bIdentical
= false;
1248 int blockstart
= -1;
1250 if ((state
!= DiffState::Edited
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
, blockstart
, blockend
)))
1253 eIcon
= TScreenedViewLine::ICN_SAME
;
1255 eIcon
= TScreenedViewLine::ICN_WHITESPACEDIFF
;
1256 if (((blockstart
>= 0) && (blockend
>= 0)) && (blockstart
< blockend
))
1258 if (nViewLine
> blockstart
)
1259 Invalidate(); // redraw the upper icons since they're now changing
1260 while (blockstart
<= blockend
)
1261 m_ScreenedViewLine
[blockstart
++].eIcon
= eIcon
;
1264 if (m_pViewData
->GetMovedIndex(nViewLine
) >= 0)
1265 eIcon
= TScreenedViewLine::ICN_MOVED
;
1266 if (m_pViewData
->GetMarked(nViewLine
))
1267 eIcon
= TScreenedViewLine::ICN_MARKED
;
1268 m_ScreenedViewLine
[nViewLine
].eIcon
= eIcon
;
1272 case TScreenedViewLine::ICN_UNKNOWN
:
1273 case TScreenedViewLine::ICN_NONE
:
1275 case TScreenedViewLine::ICN_SAME
:
1276 icon
= m_hEqualIcon
;
1278 case TScreenedViewLine::ICN_EDIT
:
1279 icon
= m_hEditedIcon
;
1281 case TScreenedViewLine::ICN_WHITESPACEDIFF
:
1282 icon
= m_hWhitespaceBlockIcon
;
1284 case TScreenedViewLine::ICN_ADD
:
1285 icon
= m_hAddedIcon
;
1287 case TScreenedViewLine::ICN_CONFLICT
:
1288 icon
= m_hConflictedIcon
;
1290 case TScreenedViewLine::ICN_CONFLICTIGNORED
:
1291 icon
= m_hConflictedIgnoredIcon
;
1293 case TScreenedViewLine::ICN_REMOVED
:
1294 icon
= m_hRemovedIcon
;
1296 case TScreenedViewLine::ICN_MOVED
:
1297 icon
= m_hMovedIcon
;
1299 case TScreenedViewLine::ICN_MARKED
:
1300 icon
= m_hMarkedIcon
;
1304 int iconWidth
= GetSystemMetrics(SM_CXSMICON
);
1305 int iconHeight
= GetSystemMetrics(SM_CYSMICON
);
1308 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2), rect
.top
+ (rect
.Height() - iconHeight
) / 2, icon
, iconWidth
, iconHeight
, 0, nullptr, DI_NORMAL
);
1310 if ((m_bViewLinenumbers
)&&(m_nDigits
))
1312 int nSubLine
= GetSubLineOffset(nLineIndex
);
1313 bool bIsFirstSubline
= (nSubLine
== 0) || (nSubLine
== -1);
1314 CString sLinenumber
;
1315 if (bIsFirstSubline
)
1317 CString sLinenumberFormat
;
1318 int nLineNumber
= GetLineNumber(nLineIndex
);
1319 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex
)))
1321 // TODO: do not show if there is no number hidden
1322 // TODO: show number if there is only one
1323 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1324 sLinenumber
.Format(sLinenumberFormat
, (m_nDigits
>1) ? L
"↕⁞" : L
"⁞"); // alternative …
1326 else if (nLineNumber
>= 0)
1328 sLinenumberFormat
.Format(L
"%%%dd", m_nDigits
);
1329 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1331 else if (m_pMainFrame
->m_bWrapLines
)
1333 sLinenumberFormat
.Format(L
"%%%ds", m_nDigits
);
1334 sLinenumber
.Format(sLinenumberFormat
, L
"·");
1336 if (!sLinenumber
.IsEmpty())
1338 pdc
->SetBkColor(CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
)));
1339 pdc
->SetTextColor(CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
));
1341 pdc
->SelectObject(GetFont());
1342 pdc
->ExtTextOut(rect
.left
+ iconWidth
+ CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2), rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, nullptr);
1349 int CBaseView::GetMarginWidth()
1351 int marginWidth
= GetSystemMetrics(SM_CXSMICON
) + CDPIAware::Instance().ScaleX(GetSafeHwnd(), 4);
1353 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1357 int nLength
= m_pViewData
->GetCount();
1358 // find out how many digits are needed to show the highest line number
1360 sMax
.Format(L
"%d", nLength
);
1361 m_nDigits
= sMax
.GetLength();
1363 int nWidth
= GetCharWidth();
1364 marginWidth
+= (m_nDigits
* nWidth
) + CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2);
1370 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1372 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1373 COLORREF crBk
, crFg
;
1374 if (IsBottomViewGood())
1376 CDiffColors::GetInstance().GetColors(DiffState::Normal
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBk
, crFg
);
1377 crBk
= CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_SCROLLBAR
));
1381 DiffState state
= DiffState::Removed
;
1382 if (this == m_pwndRight
)
1384 state
= DiffState::Added
;
1386 CDiffColors::GetInstance().GetColors(state
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBk
, crFg
);
1388 pdc
->SetBkColor(crBk
);
1389 pdc
->FillSolidRect(textrect
, crBk
);
1391 pdc
->SetTextColor(crFg
);
1393 pdc
->SelectObject(GetFont(FALSE
, TRUE
));
1398 sViewTitle
= L
"* " + m_sWindowName
;
1402 sViewTitle
= m_sWindowName
;
1404 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1405 if (nStringLength
> rect
.Width())
1407 int offset
= std::min
<int>(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1408 sViewTitle
= m_sWindowName
.Mid(offset
);
1410 RECT titleRC
= textrect
;
1411 titleRC
.left
= std::max
<int>(rect
.left
+ (rect
.Width() - nStringLength
) / 2, 1);
1412 titleRC
.top
= rect
.top
+ (HEADERHEIGHT
/ 2);
1413 pdc
->DrawText(sViewTitle
, &titleRC
, DT_HIDEPREFIX
| DT_NOPREFIX
| DT_SINGLELINE
);
1414 if (this->GetFocus() == this)
1415 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1417 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1420 void CBaseView::OnDraw(CDC
* pDC
)
1423 GetClientRect(rcClient
);
1425 int nLineCount
= GetLineCount();
1426 int nLineHeight
= GetLineHeight();
1429 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1430 if (!m_pCacheBitmap
)
1432 m_pCacheBitmap
= new CBitmap
;
1433 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1435 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1437 DrawHeader(pDC
, rcClient
);
1441 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1442 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1443 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1444 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1446 int nCurrentLine
= m_nTopLine
;
1447 bool bBeyondFileLineCached
= false;
1448 while (rcLine
.top
< rcClient
.bottom
)
1450 if (nCurrentLine
< nLineCount
)
1452 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1453 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1454 bBeyondFileLineCached
= false;
1456 else if (!bBeyondFileLineCached
)
1458 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1459 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1460 bBeyondFileLineCached
= true;
1463 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1466 rcLine
.OffsetRect(0, nLineHeight
);
1469 cacheDC
.SelectObject(pOldBitmap
);
1473 bool CBaseView::IsStateConflicted(DiffState state
)
1477 case DiffState::Conflicted
:
1478 case DiffState::Conflicted_Ignored
:
1479 case DiffState::ConflictEmpty
:
1480 case DiffState::ConflictAdded
:
1486 bool CBaseView::IsStateEmpty(DiffState state
)
1490 case DiffState::ConflictEmpty
:
1491 case DiffState::Unknown
:
1492 case DiffState::Empty
:
1498 bool CBaseView::IsStateRemoved(DiffState state
)
1502 case DiffState::Removed
:
1503 case DiffState::TheirsRemoved
:
1504 case DiffState::YoursRemoved
:
1505 case DiffState::IdenticalRemoved
:
1511 DiffState
CBaseView::ResolveState(DiffState state
)
1513 if (IsStateConflicted(state
))
1515 if (state
== DiffState::ConflictEmpty
)
1516 return DiffState::ConflictResolvedEmpty
;
1518 return DiffState::ConflictResolved
;
1524 bool CBaseView::IsLineEmpty(int nLineIndex
)
1526 if (m_pViewData
== 0)
1528 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1529 return IsViewLineEmpty(nViewLine
);
1532 bool CBaseView::IsViewLineEmpty(int nViewLine
)
1534 if (m_pViewData
== 0)
1536 const DiffState state
= m_pViewData
->GetState(nViewLine
);
1537 return IsStateEmpty(state
);
1540 bool CBaseView::IsLineRemoved(int nLineIndex
)
1542 if (m_pViewData
== 0)
1544 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1545 return IsViewLineRemoved(nViewLine
);
1548 bool CBaseView::IsViewLineRemoved(int nViewLine
)
1550 if (m_pViewData
== 0)
1552 const DiffState state
= m_pViewData
->GetState(nViewLine
);
1553 return IsStateRemoved(state
);
1556 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1559 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedDarkBk
: m_InlineAddedDarkBk
;
1560 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1563 COLORREF
CBaseView::InlineViewLineDiffColor(int nViewLine
)
1566 return IsViewLineRemoved(nViewLine
) ? m_InlineRemovedDarkBk
: m_InlineAddedDarkBk
;
1567 return IsViewLineRemoved(nViewLine
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1570 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1572 if (origin
.x
< (rc
.left
- GetCharWidth() + 1))
1574 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< GetLineCount())))
1576 int viewLine
= GetViewLineForScreen(nLineIndex
);
1577 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
1580 HICON hEndingIcon
= nullptr;
1584 hEndingIcon
= m_hLineEndingCR
;
1587 hEndingIcon
= m_hLineEndingCRLF
;
1590 hEndingIcon
= m_hLineEndingLF
;
1594 // If EOL style has changed, color end-of-line markers as inline differences.
1596 m_bShowInlineDiff
&& m_pOtherViewData
&&
1597 (viewLine
< m_pOtherViewData
->GetCount()) &&
1598 (ending
!= EOL::NoEnding
) &&
1599 (ending
!= m_pOtherViewData
->GetLineEnding(viewLine
) &&
1600 (m_pOtherViewData
->GetLineEnding(viewLine
) != EOL::NoEnding
))
1603 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1606 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), 0, nullptr, DI_NORMAL
);
1610 CPen
pen(PS_SOLID
, 0, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
1611 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1612 int yMiddle
= origin
.y
+ rc
.Height() / 2;
1613 int xMiddle
= origin
.x
+ GetCharWidth() / 2;
1614 bool bMultiline
= false;
1615 const auto onepix
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 1);
1616 const auto twopix
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2);
1617 const auto fourpix
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 4);
1618 const auto fivepix
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 5);
1619 if (((int)m_Screen2View
.size() > nLineIndex
+ 1) && (GetViewLineForScreen(nLineIndex
+ 1) == viewLine
))
1621 if (GetLineLength(nLineIndex
+ 1))
1625 pDC
->MoveTo(origin
.x
, yMiddle
- twopix
);
1626 pDC
->LineTo(origin
.x
+ GetCharWidth() - onepix
, yMiddle
- twopix
);
1627 pDC
->LineTo(origin
.x
+ GetCharWidth() - onepix
, yMiddle
+ twopix
);
1628 pDC
->LineTo(origin
.x
, yMiddle
+ twopix
);
1630 else if (GetLineLength(nLineIndex
) == 0)
1633 else if ((nLineIndex
> 0) && (GetViewLineForScreen(nLineIndex
- 1) == viewLine
) && (GetLineLength(nLineIndex
) == 0))
1642 // arrow from top to middle+2, then left
1643 pDC
->MoveTo(origin
.x
+ GetCharWidth() - onepix
, rc
.top
+ onepix
);
1644 pDC
->LineTo(origin
.x
+ GetCharWidth() - onepix
, yMiddle
);
1646 // arrow from right to left
1647 pDC
->MoveTo(origin
.x
+ GetCharWidth() - onepix
, yMiddle
);
1648 pDC
->LineTo(origin
.x
, yMiddle
);
1649 pDC
->LineTo(origin
.x
+ fourpix
, yMiddle
+ fourpix
);
1650 pDC
->MoveTo(origin
.x
, yMiddle
);
1651 pDC
->LineTo(origin
.x
+ fourpix
, yMiddle
- fourpix
);
1654 // from right-upper to left then down
1655 pDC
->MoveTo(origin
.x
+ GetCharWidth() - onepix
, yMiddle
- twopix
);
1656 pDC
->LineTo(xMiddle
, yMiddle
- twopix
);
1657 pDC
->LineTo(xMiddle
, rc
.bottom
- onepix
);
1658 pDC
->LineTo(xMiddle
+ fourpix
, rc
.bottom
- fivepix
);
1659 pDC
->MoveTo(xMiddle
, rc
.bottom
- onepix
);
1660 pDC
->LineTo(xMiddle
- fourpix
, rc
.bottom
- fivepix
);
1663 // arrow from top to bottom
1664 pDC
->MoveTo(xMiddle
, rc
.top
);
1665 pDC
->LineTo(xMiddle
, rc
.bottom
- onepix
);
1666 pDC
->LineTo(xMiddle
+ fourpix
, rc
.bottom
- fivepix
);
1667 pDC
->MoveTo(xMiddle
, rc
.bottom
- onepix
);
1668 pDC
->LineTo(xMiddle
- fourpix
, rc
.bottom
- fivepix
);
1670 case EOL::FF
: // Form Feed, U+000C
1671 case EOL::NEL
: // Next Line, U+0085
1672 case EOL::LS
: // Line Separator, U+2028
1673 case EOL::PS
: // Paragraph Separator, U+2029
1674 // draw a horizontal line at the bottom of this line
1675 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- 1, rc
.right
, rc
.bottom
, CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
));
1676 pDC
->MoveTo(origin
.x
+ GetCharWidth() - 1, rc
.bottom
- GetCharWidth() - 2);
1677 pDC
->LineTo(origin
.x
, rc
.bottom
- 2);
1678 pDC
->LineTo(origin
.x
+ 5, rc
.bottom
- 2);
1679 pDC
->MoveTo(origin
.x
, rc
.bottom
- 2);
1680 pDC
->LineTo(origin
.x
+ 1, rc
.bottom
- 6);
1682 default: // other EOLs
1683 // arrow from top right to bottom left
1684 pDC
->MoveTo(origin
.x
+ GetCharWidth() - 1, rc
.bottom
- GetCharWidth());
1685 pDC
->LineTo(origin
.x
, rc
.bottom
- 1);
1686 pDC
->LineTo(origin
.x
+ 5, rc
.bottom
- 2);
1687 pDC
->MoveTo(origin
.x
, rc
.bottom
- 1);
1688 pDC
->LineTo(origin
.x
+ 1, rc
.bottom
- 6);
1694 pDC
->SelectObject(oldpen
);
1698 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1700 if (!m_bShowSelection
)
1705 if (!GetViewSelection(nSelBlockStart
, nSelBlockEnd
))
1708 const int THICKNESS
= 2;
1709 COLORREF rectcol
= 0;
1711 rectcol
= CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
);
1713 rectcol
= CTheme::Instance().GetThemeColor(GetSysColor(COLOR_GRAYTEXT
));
1715 int nViewLineIndex
= GetViewLineForScreen(nLineIndex
);
1716 int nSubLine
= GetSubLineOffset(nLineIndex
);
1717 bool bFirstLineOfViewLine
= (nSubLine
==0 || nSubLine
==-1);
1718 if ((nViewLineIndex
== nSelBlockStart
) && bFirstLineOfViewLine
)
1720 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1723 bool bLastLineOfViewLine
= (nLineIndex
+1 == m_Screen2View
.size()) || (GetViewLineForScreen(nLineIndex
) != GetViewLineForScreen(nLineIndex
+1));
1724 if ((nViewLineIndex
== nSelBlockEnd
) && bLastLineOfViewLine
)
1726 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1730 void CBaseView::DrawTextLine(
1731 CDC
* pDC
, const CRect
&rc
, int nLineIndex
, POINT
& coords
)
1733 ASSERT(nLineIndex
< GetLineCount());
1734 int nViewLine
= GetViewLineForScreen(nLineIndex
);
1735 ASSERT(m_pViewData
&& (nViewLine
< m_pViewData
->GetCount()));
1738 rgn
.CreateRectRgn(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1739 pDC
->SelectClipRgn(&rgn
);
1740 SCOPE_EXIT
{ pDC
->SelectClipRgn(nullptr); };
1742 LineColors lineCols
= GetLineColors(nViewLine
);
1744 CString sViewLine
= GetViewLineChars(nViewLine
);
1746 if (m_bShowSelection
&& HasTextSelection())
1748 // has this line selection ?
1749 if ((m_ptSelectionViewPosStart
.y
<= nViewLine
) && (nViewLine
<= m_ptSelectionViewPosEnd
.y
))
1751 int nViewLineLength
= sViewLine
.GetLength();
1753 // first suppose the whole line is selected
1754 int selectedStart
= 0;
1755 int selectedEnd
= nViewLineLength
;
1757 // the view line is partially selected
1758 if (m_ptSelectionViewPosStart
.y
== nViewLine
)
1760 selectedStart
= m_ptSelectionViewPosStart
.x
;
1763 if (m_ptSelectionViewPosEnd
.y
== nViewLine
)
1765 selectedEnd
= m_ptSelectionViewPosEnd
.x
;
1767 // apply selection coloring
1768 // First enforce start and end point
1769 lineCols
.SplitBlock(selectedStart
);
1770 lineCols
.SplitBlock(selectedEnd
);
1771 // change color of affected parts
1772 long intenseColorScale
= m_bFocused
? 70 : 30;
1773 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(selectedStart
);
1774 for ( ; it
!= lineCols
.end() && it
->first
< selectedEnd
; ++it
)
1776 auto& second
= it
->second
;
1777 COLORREF crBk
= CAppUtils::IntenseColor(intenseColorScale
, second
.background
);
1778 if (second
.shot
== second
.background
)
1780 second
.background
= crBk
;
1781 second
.text
= CAppUtils::IntenseColor(intenseColorScale
, second
.text
);
1786 // TODO: remove duplicate from selection and mark
1787 if (!m_sMarkedWord
.IsEmpty())
1789 int nMarkLength
= m_sMarkedWord
.GetLength();
1790 //int nViewLineLength = sViewLine.GetLength();
1791 const wchar_t* text
= sViewLine
;
1792 const wchar_t* findText
= text
;
1793 while ((findText
= wcsstr(findText
, static_cast<LPCWSTR
>(m_sMarkedWord
))) != 0)
1795 int nMarkStart
= static_cast<int>(findText
- text
);
1796 int nMarkEnd
= nMarkStart
+ nMarkLength
;
1797 findText
+= nMarkLength
;
1798 ECharGroup eLeft
= GetCharGroup(sViewLine
, nMarkStart
- 1);
1799 ECharGroup eStart
= GetCharGroup(sViewLine
, nMarkStart
);
1800 if (eLeft
== eStart
)
1802 ECharGroup eRight
= GetCharGroup(sViewLine
, nMarkEnd
);
1803 ECharGroup eEnd
= GetCharGroup(sViewLine
, nMarkEnd
- 1);
1807 // First enforce start and end point
1808 lineCols
.SplitBlock(nMarkStart
);
1809 lineCols
.SplitBlock(nMarkEnd
);
1810 // change color of affected parts
1811 const long int nIntenseColorScale
= 200;
1812 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
);
1813 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
; ++it
)
1815 auto& second
= it
->second
;
1816 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1817 if (second
.shot
== second
.background
)
1819 second
.background
= crBk
;
1820 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1821 if (CTheme::Instance().IsDarkTheme())
1823 int bkGray
= (static_cast<int>(GetRValue(crBk
)) + GetGValue(crBk
) + GetBValue(crBk
)) / 3;
1824 int frGray
= (static_cast<int>(GetRValue(second
.text
)) + GetGValue(second
.text
) + GetBValue(second
.text
)) / 3;
1825 if (abs(bkGray
- frGray
) < 100)
1826 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1831 if (!m_sFindText
.IsEmpty())
1835 int nStringPos
= nMarkStart
;
1836 CString searchLine
= sViewLine
;
1838 searchLine
.MakeLower();
1839 while (StringFound(searchLine
, SearchNext
, nMarkStart
, nMarkEnd
)!=0)
1841 // First enforce start and end point
1842 lineCols
.SplitBlock(nMarkStart
+nStringPos
);
1843 lineCols
.SplitBlock(nMarkEnd
+nStringPos
);
1844 // change color of affected parts
1845 const long int nIntenseColorScale
= 30;
1846 std::map
<int, linecolors_t
>::iterator it
= lineCols
.lower_bound(nMarkStart
+nStringPos
);
1847 for ( ; it
!= lineCols
.end() && it
->first
< nMarkEnd
+ nStringPos
; ++it
)
1849 auto& second
= it
->second
;
1850 COLORREF crBk
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.background
);
1851 if (second
.shot
== second
.background
)
1853 second
.background
= crBk
;
1854 second
.text
= CAppUtils::IntenseColor(nIntenseColorScale
, second
.text
);
1856 searchLine
= searchLine
.Mid(nMarkEnd
);
1857 nStringPos
+= nMarkEnd
;
1863 // @ this point we may cache data for next line which may be same in wrapped mode
1865 int nTextOffset
= 0;
1866 int nSubline
= GetSubLineOffset(nLineIndex
);
1867 for (int n
=0; n
<nSubline
; n
++)
1869 CString sLine
= m_ScreenedViewLine
[nViewLine
].SubLines
[n
];
1870 nTextOffset
+= sLine
.GetLength();
1873 CString sLine
= GetLineChars(nLineIndex
);
1874 int nLineLength
= sLine
.GetLength();
1875 CString sLineExp
= ExpandChars(sLine
);
1876 LPCWSTR textExp
= sLineExp
;
1877 //int nLineLengthExp = sLineExp.GetLength();
1879 int nLeft
= coords
.x
;
1880 for (std::map
<int, linecolors_t
>::const_iterator itStart
= lineCols
.begin(); itStart
!= lineCols
.end(); ++itStart
)
1882 std::map
<int, linecolors_t
>::const_iterator itEnd
= itStart
;
1884 int nStart
= std::max
<int>(0, itStart
->first
- nTextOffset
);
1885 int nEnd
= nLineLength
;
1886 if (itEnd
!= lineCols
.end())
1888 nEnd
= std::min
<int>(nEnd
, itEnd
->first
- nTextOffset
);
1890 int nBlockLength
= nEnd
- nStart
;
1891 if (nBlockLength
> 0 && nEnd
>=0)
1893 auto& second
= itStart
->second
;
1894 pDC
->SetBkColor(second
.background
);
1895 pDC
->SetTextColor(second
.text
);
1896 int nEndExp
= CountExpandedChars(sLine
, nEnd
);
1897 int nTextLength
= nEndExp
- nStartExp
;
1898 LPCWSTR p_zBlockText
= textExp
+ nStartExp
;
1900 GetTextExtentPoint32(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, &Size
); // falls time-2-tme
1901 int nRight
= nLeft
+ Size
.cx
;
1902 if ((nRight
> rc
.left
) && (nLeft
< rc
.right
))
1904 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1905 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1906 // is 4094 (4095 doesn't work anymore).
1907 // So we limit the length here to that 4094 chars.
1908 // In case we're scrolled to the right, there's no need to draw the string
1909 // from way outside our window, so we also offset the drawing to the start of the window.
1910 // This reduces the string length as well.
1912 int leftcoord
= nLeft
;
1915 int fit
= nTextLength
;
1916 auto posBuffer
= std::make_unique
<int[]>(fit
);
1917 GetTextExtentExPoint(pDC
->GetSafeHdc(), p_zBlockText
, nTextLength
, INT_MAX
, &fit
, posBuffer
.get(), &Size
);
1918 int lower
= 0, upper
= fit
- 1;
1921 int middle
= (upper
+ lower
+ 1) / 2;
1922 int width
= posBuffer
[middle
];
1923 if (rc
.left
- nLeft
< width
)
1927 } while (lower
< upper
);
1930 nTextLength
-= offset
;
1931 leftcoord
+= lower
> 0 ? posBuffer
[lower
- 1] : 0;
1935 drawRC
.left
= leftcoord
;
1936 drawRC
.top
= coords
.y
;
1937 pDC
->DrawText(p_zBlockText
+ offset
, min(nTextLength
, 4094), &drawRC
, DT_HIDEPREFIX
| DT_NOPREFIX
| DT_SINGLELINE
);
1938 if ((second
.shot
!= second
.background
) && (itStart
->first
== nStart
+ nTextOffset
))
1939 pDC
->FillSolidRect(nLeft
- 1, rc
.top
, 1, rc
.Height(), second
.shot
);
1943 nStartExp
= nEndExp
;
1948 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1950 if (nLineIndex
>= GetLineCount())
1952 ASSERT(nLineIndex
>= -1);
1954 if ((nLineIndex
== -1) || !m_pViewData
)
1956 // Draw line beyond the text
1957 COLORREF crBkgnd
, crText
;
1958 CDiffColors::GetInstance().GetColors(DiffState::Unknown
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
1959 pDC
->FillSolidRect(rc
, crBkgnd
);
1963 int viewLine
= GetViewLineForScreen(nLineIndex
);
1964 if (m_pMainFrame
->m_bCollapsed
)
1966 if (m_pViewData
->GetHideState(viewLine
) == HideState::Marker
)
1968 COLORREF crBkgnd
, crText
;
1969 CDiffColors::GetInstance().GetColors(DiffState::Unknown
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
1970 pDC
->FillSolidRect(rc
, crBkgnd
);
1972 const int THICKNESS
= 2;
1973 COLORREF rectcol
= CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: GetSysColor(COLOR_WINDOWTEXT
);
1974 pDC
->FillSolidRect(rc
.left
, rc
.top
+ (rc
.Height()/2), rc
.Width(), THICKNESS
, rectcol
);
1975 pDC
->SetTextColor(CTheme::Instance().GetThemeColor(GetSysColor(COLOR_GRAYTEXT
)));
1976 pDC
->SetBkColor(crBkgnd
);
1978 pDC
->DrawText(L
"{...}", &rect
, DT_NOPREFIX
|DT_SINGLELINE
|DT_CENTER
);
1983 DiffState diffState
= m_pViewData
->GetState(viewLine
);
1984 COLORREF crBkgnd
, crText
;
1985 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
1987 if (diffState
== DiffState::Conflicted
)
1989 // conflicted lines are shown without 'text' on them
1991 pDC
->FillSolidRect(rc
, crBkgnd
);
1992 // now draw some faint text patterns
1993 pDC
->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd
));
1994 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
1995 DrawBlockLine(pDC
, rc
, nLineIndex
);
1999 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
2000 CString sLine
= GetLineChars(nLineIndex
);
2001 if (sLine
.IsEmpty())
2003 pDC
->FillSolidRect(rc
, crBkgnd
);
2004 DrawBlockLine(pDC
, rc
, nLineIndex
);
2005 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2013 pDC
->SelectObject(GetFont(FALSE
, FALSE
));
2015 DrawTextLine(pDC
, rc
, nLineIndex
, origin
);
2017 // draw white space after the end of line
2019 if (origin
.x
> frect
.left
)
2020 frect
.left
= origin
.x
;
2021 if (frect
.right
> frect
.left
)
2022 pDC
->FillSolidRect(frect
, crBkgnd
);
2024 // draw the whitespace chars
2025 auto pszChars
= static_cast<LPCWSTR
>(sLine
);
2026 if (m_bViewWhitespace
)
2030 LPCWSTR pLastSpace
= pszChars
;
2031 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
2032 xpos
-= m_nOffsetChar
* GetCharWidth();
2034 CPen
pen(PS_SOLID
, 0, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
2035 const auto twopix
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2);
2036 const auto fourpixY
= CDPIAware::Instance().ScaleY(GetSafeHwnd(), 4);
2037 const auto sixpixY
= CDPIAware::Instance().ScaleX(GetSafeHwnd(), 5);
2044 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2045 pLastSpace
= pszChars
+ 1;
2047 int nSpaces
= GetTabSize() - nChars
% GetTabSize();
2048 if (xpos
+ nSpaces
* GetCharWidth() > 0)
2050 int xposreal
= max(xpos
, 0);
2051 if ((xposreal
> 0) || (nSpaces
> 0))
2053 CPen
* oldPen
= pDC
->SelectObject(&pen
);
2054 pDC
->MoveTo(xposreal
+ rc
.left
+ twopix
, y
);
2055 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- twopix
, y
);
2056 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- sixpixY
, y
- fourpixY
);
2057 pDC
->MoveTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- twopix
, y
);
2058 pDC
->LineTo((xpos
+ nSpaces
* GetCharWidth()) + rc
.left
- sixpixY
, y
+ fourpixY
);
2059 pDC
->SelectObject(oldPen
);
2062 xpos
+= nSpaces
* GetCharWidth();
2068 xpos
+= pDC
->GetTextExtent(pLastSpace
, static_cast<int>(pszChars
- pLastSpace
)).cx
;
2069 pLastSpace
= pszChars
+ 1;
2072 const int cxWhitespace
= twopix
;
2073 const int cyWhitespace
= twopix
;
2074 // draw 2-logical pixel rectangle, like Scintilla editor.
2075 pDC
->FillSolidRect(xpos
+ rc
.left
+ GetCharWidth() / 2 - cxWhitespace
/2, y
, cxWhitespace
, cyWhitespace
, CTheme::Instance().GetThemeColor(m_WhiteSpaceFg
));
2077 xpos
+= GetCharWidth();
2088 DrawBlockLine(pDC
, rc
, nLineIndex
);
2089 if (origin
.x
>= rc
.left
)
2090 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
2093 void CBaseView::ExpandChars(const CString
&sLine
, int nOffset
, int nCount
, CString
&line
)
2101 int nTabSize
= GetTabSize();
2103 int nActualOffset
= CountExpandedChars(sLine
, nOffset
);
2105 auto pszChars
= static_cast<LPCWSTR
>(sLine
);
2106 pszChars
+= nOffset
;
2107 int nLength
= nCount
;
2110 for (int i
=0; i
<nLength
; i
++)
2112 if (pszChars
[i
] == L
'\t')
2116 LPWSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
2118 if (nTabCount
> 0 || m_bViewWhitespace
)
2120 for (int i
=0; i
<nLength
; i
++)
2122 if (pszChars
[i
] == L
'\t')
2124 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
2127 pszBuf
[nCurPos
++] = L
' ';
2133 pszBuf
[nCurPos
] = pszChars
[i
];
2140 memcpy(pszBuf
, pszChars
, sizeof(wchar_t) * nLength
);
2143 pszBuf
[nCurPos
] = 0;
2144 line
.ReleaseBuffer();
2147 CString
CBaseView::ExpandChars(const CString
&sLine
, int nOffset
)
2150 int nLength
= sLine
.GetLength();
2151 ExpandChars(sLine
, nOffset
, nLength
, sRet
);
2155 int CBaseView::CountExpandedChars(const CString
&sLine
, int nLength
)
2157 int nTabSize
= GetTabSize();
2159 int nActualOffset
= 0;
2160 for (int i
=0; i
<nLength
; i
++)
2162 if (sLine
[i
] == L
'\t')
2163 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
2167 return nActualOffset
;
2170 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
2173 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2175 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2177 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
2179 m_pwndLocator
->Invalidate();
2182 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
2184 //almost the same as ScrollAllToLine, but try to put the line in the
2185 //middle of the view, not on top
2186 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
2187 if (nNewTopLine
< 0)
2189 if (nNewTopLine
>= m_Screen2View
.size())
2190 nNewTopLine
= m_Screen2View
.size() - 1;
2192 ScrollAllToLine(nNewTopLine
);
2194 ScrollToLine(nNewTopLine
);
2197 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
2202 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
2204 if (CView::OnCreate(lpCreateStruct
) == -1)
2207 SecureZeroMemory(&m_lfBaseFont
, sizeof(m_lfBaseFont
));
2208 //lstrcpy(m_lfBaseFont.lfFaceName, L"Courier New");
2209 //lstrcpy(m_lfBaseFont.lfFaceName, L"FixedSys");
2210 m_lfBaseFont
.lfHeight
= 0;
2211 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
2212 m_lfBaseFont
.lfItalic
= FALSE
;
2213 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
2214 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
2215 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
2216 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
2217 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
2222 void CBaseView::OnDestroy()
2229 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
2231 CView::OnSize(nType
, cx
, cy
);
2234 m_nScreenLines
= -1;
2235 m_nScreenChars
= -1;
2237 if (m_nLastScreenChars
!= GetScreenChars())
2239 auto oldCaretLine
= m_ptCaretViewPos
.y
;
2240 m_nLastScreenChars
= m_nScreenChars
;
2241 BuildAllScreen2ViewVector();
2242 if (m_pMainFrame
&& m_pMainFrame
->m_bWrapLines
)
2244 ScrollToLine(oldCaretLine
, false);
2245 EnsureCaretVisible();
2246 // if we're in wrap mode, the line wrapping most likely changed
2247 // and that means we have to redraw the whole window, not just the
2253 // make sure the view header is redrawn
2255 GetClientRect(&rcScroll
);
2256 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2257 InvalidateRect(&rcScroll
, FALSE
);
2262 // make sure the view header is redrawn
2264 GetClientRect(&rcScroll
);
2265 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
2266 InvalidateRect(&rcScroll
, FALSE
);
2269 RecalcVertScrollBar();
2270 RecalcHorzScrollBar();
2275 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2278 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2280 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2282 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
2284 m_pwndLocator
->Invalidate();
2285 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
2288 void CBaseView::OnMouseHWheel(UINT nFlags
, short zDelta
, CPoint pt
)
2291 m_pwndLeft
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2293 m_pwndRight
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2295 m_pwndBottom
->OnDoMouseHWheel(nFlags
, zDelta
, pt
);
2297 m_pwndLocator
->Invalidate();
2300 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2302 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2303 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2305 if (bControl
|| bShift
)
2307 if (m_pMainFrame
->m_bWrapLines
)
2309 // Ctrl-Wheel scrolls sideways
2310 ScrollSide(-zDelta
/30);
2314 ScrollVertical(zDelta
);
2318 void CBaseView::OnDoMouseHWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
2320 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2321 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2323 if (bControl
|| bShift
)
2325 // Ctrl-H-Wheel scrolls vertical
2326 ScrollVertical(zDelta
);
2330 if (m_pMainFrame
->m_bWrapLines
)
2332 // Ctrl-Wheel scrolls sideways
2333 ScrollSide(zDelta
/30);
2337 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
2339 if (nHitTest
== HTCLIENT
)
2341 if ((m_pViewData
)&&(m_pMainFrame
->m_bCollapsed
))
2343 if (m_nMouseLine
< m_Screen2View
.size())
2345 if (m_nMouseLine
>= 0)
2347 int viewLine
= GetViewLineForScreen(m_nMouseLine
);
2348 if (viewLine
< m_pViewData
->GetCount())
2350 if (m_pViewData
->GetHideState(viewLine
) == HideState::Marker
)
2352 ::SetCursor(::LoadCursor(nullptr, IDC_HAND
));
2359 if (m_mouseInMargin
)
2361 ::SetCursor(m_margincursor
);
2364 if (m_nMouseLine
>= 0)
2366 ::SetCursor(::LoadCursor(nullptr, IDC_IBEAM
)); // Set To Edit Cursor
2370 ::SetCursor(::LoadCursor(nullptr, IDC_ARROW
)); // Set To Arrow Cursor
2373 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
2376 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
2378 CView::OnKillFocus(pNewWnd
);
2384 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
2386 CView::OnSetFocus(pOldWnd
);
2392 int CBaseView::GetLineFromPoint(CPoint point
)
2394 ScreenToClient(&point
);
2395 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2398 void CBaseView::OnContextMenu(CPoint point
, DiffState state
)
2401 GetClientRect(rcClient
);
2402 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+ HEADERHEIGHT
);
2404 CRect
borderrect(rcClient
.left
, rcClient
.top
+ m_nLineHeight
+ HEADERHEIGHT
, 0, rcClient
.bottom
);
2406 CPoint ptLocal
= point
;
2407 ScreenToClient(&ptLocal
);
2409 if (textrect
.PtInRect(ptLocal
) || borderrect
.PtInRect(ptLocal
))
2411 // inside the header part of the view (showing the filename)
2412 if (IsViewGood(m_pwndBottom
))
2415 if (this == m_pwndLeft
)
2418 if (!popup
.CreatePopupMenu())
2421 temp
.LoadString(IDS_HEADER_DIFFLEFTTOBASE
);
2422 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2423 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2425 m_pMainFrame
->DiffLeftToBase();
2427 if (this == m_pwndRight
)
2430 if (!popup
.CreatePopupMenu())
2433 temp
.LoadString(IDS_HEADER_DIFFRIGHTTOBASE
);
2434 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 10, temp
);
2435 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2437 m_pMainFrame
->DiffRightToBase();
2443 if (!this->IsWindowVisible())
2447 if (!popup
.CreatePopupMenu())
2450 AddContextItems(popup
, state
);
2454 int nEncodingCommandBase
= POPUPCOMMAND__LAST
;
2455 int nEolCommandBase
= nEncodingCommandBase
+_countof(uctArray
);
2459 TWhitecharsProperties oWhites
= GetWhitecharsProperties();
2460 temp
.LoadString(IDS_EDIT_TAB2SPACE
);
2461 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTabsToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_TABTOSPACES
, temp
);
2462 temp
.LoadString(IDS_EDIT_SPACE2TAB
);
2463 popup
.AppendMenu(MF_STRING
| (oWhites
.HasSpacesToConvert
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_SPACESTOTABS
, temp
);
2464 temp
.LoadString(IDS_EDIT_TRIM
);
2465 popup
.AppendMenu(MF_STRING
| (oWhites
.HasTrailWhiteChars
? MF_ENABLED
: (MF_DISABLED
|MF_GRAYED
)), POPUPCOMMAND_REMOVETRAILWHITES
, temp
);
2468 if (!popupEols
.CreatePopupMenu())
2471 EOL eEolType
= GetLineEndings(oWhites
.HasMixedEols
);
2472 for (int i
= 1; i
< _countof(eolArray
); i
++)
2474 temp
= GetEolName(eolArray
[i
]);
2475 bool bChecked
= (eEolType
== eolArray
[i
]);
2476 popupEols
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEolCommandBase
+i
, temp
);
2479 temp
.LoadString(IDS_VIEWCONTEXTMENU_EOL
);
2480 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupEols
.GetSafeHmenu()), temp
);
2482 // add encoding submenu
2483 if (!popupUnicode
.CreatePopupMenu())
2485 for (int i
= 0; i
< _countof(uctArray
); i
++)
2487 temp
= CFileTextLines::GetEncodingName(uctArray
[i
]);
2488 bool bChecked
= (m_texttype
== uctArray
[i
]);
2489 popupUnicode
.AppendMenu(MF_STRING
| MF_ENABLED
| (bChecked
? MF_CHECKED
: 0), nEncodingCommandBase
+i
, temp
);
2491 temp
.LoadString(IDS_VIEWCONTEXTMENU_ENCODING
);
2492 popup
.AppendMenuW(MF_POPUP
| MF_ENABLED
, reinterpret_cast<UINT_PTR
>(popupUnicode
.GetSafeHmenu()), temp
);
2496 CompensateForKeyboard(point
);
2498 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this);
2500 if (cmd
>= nEncodingCommandBase
&& (cmd
< nEncodingCommandBase
+ static_cast<int>(_countof(uctArray
))))
2502 SetTextType(uctArray
[cmd
-nEncodingCommandBase
]);
2504 if (cmd
>= nEolCommandBase
&& (cmd
< nEolCommandBase
+ static_cast<int>(_countof(eolArray
))))
2506 ReplaceLineEndings(eolArray
[cmd
-nEolCommandBase
]);
2511 // 2-pane view commands; target is right view
2512 case POPUPCOMMAND_USELEFTBLOCK
:
2513 m_pwndRight
->UseLeftBlock();
2515 case POPUPCOMMAND_USELEFTFILE
:
2516 m_pwndRight
->UseLeftFile();
2518 case POPUPCOMMAND_USEBOTHLEFTFIRST
:
2519 m_pwndRight
->UseBothLeftFirst();
2521 case POPUPCOMMAND_USEBOTHRIGHTFIRST
:
2522 m_pwndRight
->UseBothRightFirst();
2524 case POPUPCOMMAND_MARKBLOCK
:
2525 m_pwndRight
->MarkBlock(true);
2527 case POPUPCOMMAND_UNMARKBLOCK
:
2528 m_pwndRight
->MarkBlock(false);
2530 case POPUPCOMMAND_LEAVEONLYMARKEDBLOCKS
:
2531 m_pwndRight
->LeaveOnlyMarkedBlocks();
2533 // 2-pane view multiedit commands; target is left view
2534 case POPUPCOMMAND_PREPENDFROMRIGHT
:
2535 if (!m_pwndLeft
->IsReadonly())
2536 m_pwndLeft
->UseBothRightFirst();
2538 case POPUPCOMMAND_REPLACEBYRIGHT
:
2539 if (!m_pwndLeft
->IsReadonly())
2540 m_pwndLeft
->UseRightBlock();
2542 case POPUPCOMMAND_APPENDFROMRIGHT
:
2543 if (!m_pwndLeft
->IsReadonly())
2544 m_pwndLeft
->UseBothLeftFirst();
2546 case POPUPCOMMAND_USERIGHTFILE
:
2547 m_pwndLeft
->UseRightFile();
2549 // 3-pane view commands; target is bottom view
2550 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK
:
2551 m_pwndBottom
->UseBothRightFirst();
2553 case POPUPCOMMAND_USETHEIRANDYOURBLOCK
:
2554 m_pwndBottom
->UseBothLeftFirst();
2556 case POPUPCOMMAND_USEYOURBLOCK
:
2557 m_pwndBottom
->UseRightBlock();
2559 case POPUPCOMMAND_USEYOURFILE
:
2560 m_pwndBottom
->UseRightFile();
2562 case POPUPCOMMAND_USETHEIRBLOCK
:
2563 m_pwndBottom
->UseLeftBlock();
2565 case POPUPCOMMAND_USETHEIRFILE
:
2566 m_pwndBottom
->UseLeftFile();
2568 // copy, cut and paste commands
2578 // white chars manipulations
2579 case POPUPCOMMAND_TABTOSPACES
:
2580 ConvertTabToSpaces();
2582 case POPUPCOMMAND_SPACESTOTABS
:
2585 case POPUPCOMMAND_REMOVETRAILWHITES
:
2586 RemoveTrailWhiteChars();
2591 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2595 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
2600 int nViewBlockStart
= -1;
2601 int nViewBlockEnd
= -1;
2602 GetViewSelection(nViewBlockStart
, nViewBlockEnd
);
2603 if ((point
.x
!= -1) && (point
.y
!= -1))
2605 int nLine
= GetLineFromPoint(point
)-1;
2606 if ((nLine
>= 0) && (nLine
< m_Screen2View
.size()))
2608 int nViewLine
= GetViewLineForScreen(nLine
);
2609 if (((nViewLine
< nViewBlockStart
) || (nViewBlockEnd
< nViewLine
)))
2611 ClearSelection(); // Clear text-copy selection
2613 nViewBlockStart
= nViewLine
;
2614 nViewBlockEnd
= nViewLine
;
2615 DiffState state
= m_pViewData
->GetState(nViewLine
);
2616 while (nViewBlockStart
> 0)
2618 const DiffState lineState
= m_pViewData
->GetState(nViewBlockStart
- 1);
2619 if (!LinesInOneChange(-1, state
, lineState
))
2624 while (nViewBlockEnd
< (m_pViewData
->GetCount()-1))
2626 const DiffState lineState
= m_pViewData
->GetState(nViewBlockEnd
+ 1);
2627 if (!LinesInOneChange(1, state
, lineState
))
2632 SetupAllViewSelection(nViewBlockStart
, nViewBlockEnd
);
2633 UpdateCaretPosition(SetupPoint(0, nViewLine
));
2638 // FixSelection(); fix selection range
2639 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2640 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2642 DiffState state
= DiffState::Unknown
;
2643 if (GetViewSelection(nViewBlockStart
, nViewBlockEnd
))
2645 // find a more 'relevant' state in the selection
2646 for (int i
=nViewBlockStart
; i
<=nViewBlockEnd
; ++i
)
2648 state
= m_pViewData
->GetState(i
);
2649 if ((state
!= DiffState::Normal
) && (state
!= DiffState::Unknown
))
2653 OnContextMenu(point
, state
);
2656 void CBaseView::RefreshViews()
2660 m_pwndLeft
->UpdateStatusBar();
2661 m_pwndLeft
->UpdateCaret();
2662 m_pwndLeft
->Invalidate();
2666 m_pwndRight
->UpdateStatusBar();
2667 m_pwndRight
->UpdateCaret();
2668 m_pwndRight
->Invalidate();
2672 m_pwndBottom
->UpdateStatusBar();
2673 m_pwndBottom
->UpdateCaret();
2674 m_pwndBottom
->Invalidate();
2677 m_pwndLocator
->Invalidate();
2680 void CBaseView::GoToFirstDifference()
2682 SetCaretToFirstViewLine();
2683 SelectNextBlock(1, false, false);
2686 void CBaseView::GoToFirstConflict()
2688 SetCaretToFirstViewLine();
2689 SelectNextBlock(1, true, false);
2692 void CBaseView::HighlightViewLines(int nStart
, int nEnd
/* = -1 */)
2695 SetupAllViewSelection(nStart
, max(nStart
, nEnd
));
2697 UpdateCaretViewPosition(SetupPoint(0, nStart
));
2701 void CBaseView::SetupAllViewSelection(int start
, int end
)
2703 SetupViewSelection(m_pwndBottom
, start
, end
);
2704 SetupViewSelection(m_pwndLeft
, start
, end
);
2705 SetupViewSelection(m_pwndRight
, start
, end
);
2708 void CBaseView::SetupAllSelection(int start
, int end
)
2710 SetupAllViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2713 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2715 void CBaseView::SetupSelection(int start
, int end
)
2717 SetupViewSelection(GetViewLineForScreen(start
), GetViewLineForScreen(end
));
2720 void CBaseView::SetupViewSelection(CBaseView
* view
, int start
, int end
)
2722 if (!IsViewGood(view
))
2724 view
->SetupViewSelection(start
, end
);
2727 void CBaseView::SetupViewSelection(int start
, int end
)
2729 // clear text selection before setting line selection ?
2730 m_nSelViewBlockStart
= start
;
2731 m_nSelViewBlockEnd
= end
;
2736 void CBaseView::OnMergePreviousconflict()
2738 SelectNextBlock(-1, true);
2741 void CBaseView::OnMergeNextconflict()
2743 SelectNextBlock(1, true);
2746 void CBaseView::OnMergeNextdifference()
2748 SelectNextBlock(1, false);
2751 void CBaseView::OnMergePreviousdifference()
2753 SelectNextBlock(-1, false);
2756 bool CBaseView::HasNextConflict()
2758 return SelectNextBlock(1, true, true, true);
2761 bool CBaseView::HasPrevConflict()
2763 return SelectNextBlock(-1, true, true, true);
2766 bool CBaseView::HasNextDiff()
2768 return SelectNextBlock(1, false, true, true);
2771 bool CBaseView::HasPrevDiff()
2773 return SelectNextBlock(-1, false, true, true);
2776 bool CBaseView::LinesInOneChange(int direction
,
2777 DiffState initialLineState
, DiffState currentLineState
)
2779 // Checks whether all the adjacent lines starting from the initial line
2780 // and up to the current line form the single change
2782 // First of all, if the two lines have identical states, they surely
2783 // belong to one change.
2784 if (initialLineState
== currentLineState
)
2787 // Either we move down and initial line state is "added" or "removed" and
2788 // current line state is "empty"...
2791 if (currentLineState
== DiffState::Empty
)
2793 if (initialLineState
== DiffState::Added
|| initialLineState
== DiffState::Removed
)
2796 if (initialLineState
== DiffState::ConflictAdded
&& currentLineState
== DiffState::ConflictEmpty
)
2799 // ...or we move up and initial line state is "empty" and current line
2800 // state is "added" or "removed".
2803 if (initialLineState
== DiffState::Empty
)
2805 if (currentLineState
== DiffState::Added
|| currentLineState
== DiffState::Removed
)
2808 if (initialLineState
== DiffState::ConflictEmpty
&& currentLineState
== DiffState::ConflictAdded
)
2814 bool CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */, bool dryrun
/* = false */)
2819 const int linesCount
= m_Screen2View
.size();
2823 int nCenterPos
= GetCaretPosition().y
;
2826 nLimit
= linesCount
;
2828 if (nCenterPos
>= linesCount
)
2829 nCenterPos
= linesCount
-1;
2831 if (bSkipEndOfCurrentBlock
)
2833 // Find end of current block
2834 const DiffState state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2835 while (nCenterPos
!= nLimit
)
2837 const DiffState lineState
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2838 if (!LinesInOneChange(nDirection
, state
, lineState
))
2840 nCenterPos
+= nDirection
;
2844 // Find next diff/conflict block
2845 while (nCenterPos
!= nLimit
)
2847 DiffState linestate
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2849 (linestate
!= DiffState::Normal
) &&
2850 (linestate
!= DiffState::Unknown
) &&
2851 (linestate
!= DiffState::FilteredDiff
))
2856 ((linestate
== DiffState::ConflictAdded
) ||
2857 (linestate
== DiffState::Conflicted_Ignored
) ||
2858 (linestate
== DiffState::Conflicted
) ||
2859 (linestate
== DiffState::ConflictEmpty
)))
2864 nCenterPos
+= nDirection
;
2866 if (nCenterPos
== nLimit
)
2869 return (nCenterPos
!= nLimit
);
2871 // Find end of new block
2872 DiffState state
= m_pViewData
->GetState(GetViewLineForScreen(nCenterPos
));
2873 int nBlockEnd
= nCenterPos
;
2874 const int maxAllowedLine
= nLimit
-nDirection
;
2875 while (nBlockEnd
!= maxAllowedLine
)
2877 const int lineIndex
= nBlockEnd
+ nDirection
;
2878 if (lineIndex
>= linesCount
)
2880 DiffState lineState
= m_pViewData
->GetState(GetViewLineForScreen(lineIndex
));
2881 if (!LinesInOneChange(nDirection
, state
, lineState
))
2883 nBlockEnd
+= nDirection
;
2886 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
2890 POINT ptCaretPos
= {0, nCenterPos
};
2891 SetCaretPosition(ptCaretPos
);
2894 SetupAllSelection(nCenterPos
, nBlockEnd
);
2896 SetupAllSelection(nBlockEnd
, nCenterPos
);
2898 ScrollAllToLine(nTopPos
, FALSE
);
2899 RecalcAllVertScrollBars(TRUE
);
2900 SetCaretToLineStart();
2901 EnsureCaretVisible();
2902 OnNavigateNextinlinediff();
2904 UpdateViewsCaretPosition();
2906 ShowDiffLines(nCenterPos
);
2910 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
2912 if (pNMHDR
->idFrom
!= reinterpret_cast<UINT_PTR
>(m_hWnd
))
2916 strTipText
= m_sWindowName
+ L
"\r\n" + m_sFullFilePath
;
2918 DWORD pos
= GetMessagePos();
2919 CPoint
point(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
));
2920 ScreenToClient(&point
);
2921 const int nLine
= GetButtonEventLineIndex(point
);
2925 int nViewLine
= GetViewLineForScreen(nLine
);
2926 if((m_pViewData
)&&(nViewLine
< m_pViewData
->GetCount()))
2928 auto movedIndex
= m_pViewData
->GetMovedIndex(nViewLine
);
2929 if (movedIndex
>= 0)
2931 if (m_pViewData
->IsMovedFrom(nViewLine
))
2933 strTipText
.Format(IDS_MOVED_TO_TT
, movedIndex
+1);
2937 strTipText
.Format(IDS_MOVED_FROM_TT
, movedIndex
+1);
2945 if (strTipText
.IsEmpty())
2948 // need to handle both ANSI and UNICODE versions of the message
2949 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2951 auto pTTTA
= reinterpret_cast<TOOLTIPTEXTA
*>(pNMHDR
);
2952 pTTTA
->lpszText
= m_szTip
;
2953 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2957 auto pTTTW
= reinterpret_cast<TOOLTIPTEXTW
*>(pNMHDR
);
2958 lstrcpyn(m_wszTip
, strTipText
, min(strTipText
.GetLength() + 1, static_cast<int>(_countof(m_wszTip
)) - 1));
2959 pTTTW
->lpszText
= m_wszTip
;
2962 return TRUE
; // message was handled
2965 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2968 GetClientRect(rcClient
);
2969 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2971 int marginwidth
= GetSystemMetrics(SM_CXSMICON
) + CDPIAware::Instance().ScaleX(GetSafeHwnd(), 4);
2972 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount())&&(m_nDigits
> 0))
2974 marginwidth
+= (m_nDigits
* m_nCharWidth
) + CDPIAware::Instance().ScaleX(GetSafeHwnd(), 2);
2976 CRect
borderrect(rcClient
.left
, rcClient
.top
+m_nLineHeight
+HEADERHEIGHT
, marginwidth
, rcClient
.bottom
);
2978 if (textrect
.PtInRect(point
) || borderrect
.PtInRect(point
))
2980 // inside the header part of the view (showing the filename)
2981 pTI
->hwnd
= this->m_hWnd
;
2982 this->GetClientRect(&pTI
->rect
);
2983 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
2984 pTI
->uId
= reinterpret_cast<UINT_PTR
>(m_hWnd
);
2985 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
2987 // we want multi line tooltips
2988 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
2989 if (pToolTip
->GetSafeHwnd())
2990 pToolTip
->SetMaxTipWidth(SHRT_MAX
);
2992 return (textrect
.PtInRect(point
) ? 1 : 2);
2998 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
3000 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
3001 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3008 if (this==m_pwndLeft
)
3010 if (IsViewGood(m_pwndRight
))
3012 m_pwndRight
->SetFocus();
3014 else if (IsViewGood(m_pwndBottom
))
3016 m_pwndBottom
->SetFocus();
3019 else if (this==m_pwndRight
)
3021 if (IsViewGood(m_pwndBottom
))
3023 m_pwndBottom
->SetFocus();
3025 else if (IsViewGood(m_pwndLeft
))
3027 m_pwndLeft
->SetFocus();
3030 else if (this==m_pwndBottom
)
3032 if (IsViewGood(m_pwndLeft
))
3034 m_pwndLeft
->SetFocus();
3036 else if (IsViewGood(m_pwndRight
))
3038 m_pwndRight
->SetFocus();
3045 POINT ptCaretPos
= GetCaretPosition();
3046 ptCaretPos
.y
-= GetScreenLines();
3047 ptCaretPos
.y
= max(ptCaretPos
.y
, 0l);
3048 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
, false);
3049 SetCaretPosition(ptCaretPos
);
3050 OnCaretMove(MOVELEFT
, bShift
);
3051 ShowDiffLines(ptCaretPos
.y
);
3056 POINT ptCaretPos
= GetCaretPosition();
3057 ptCaretPos
.y
+= GetScreenLines();
3058 if (ptCaretPos
.y
>= GetLineCount())
3059 ptCaretPos
.y
= GetLineCount()-1;
3060 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
, false);
3061 SetCaretPosition(ptCaretPos
);
3062 OnCaretMove(MOVERIGHT
, bShift
);
3063 ShowDiffLines(ptCaretPos
.y
);
3072 SetCaretToViewStart();
3073 m_nCaretGoalPos
= 0;
3075 AdjustSelection(MOVELEFT
);
3082 POINT ptCaretPos
= GetCaretPosition();
3083 CString sLine
= GetLineChars(ptCaretPos
.y
);
3085 while (pos
< sLine
.GetLength())
3087 if (sLine
[pos
] != ' ' && sLine
[pos
] != '\t')
3091 if (ptCaretPos
.x
== pos
)
3093 SetCaretToLineStart();
3094 m_nCaretGoalPos
= 0;
3095 OnCaretMove(MOVERIGHT
, bShift
);
3101 SetCaretAndGoalPosition(ptCaretPos
);
3102 OnCaretMove(MOVELEFT
, bShift
);
3111 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
3113 ptCaretPos
.y
= GetLineCount()-1;
3114 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3115 SetCaretAndGoalPosition(ptCaretPos
);
3117 AdjustSelection(MOVERIGHT
);
3123 POINT ptCaretPos
= GetCaretPosition();
3124 ptCaretPos
.x
= GetLineLength(ptCaretPos
.y
);
3125 if ((GetSubLineOffset(ptCaretPos
.y
) != -1) && (GetSubLineOffset(ptCaretPos
.y
) != CountMultiLines(GetViewLineForScreen(ptCaretPos
.y
))-1)) // not last screen line of view line
3129 SetCaretAndGoalPosition(ptCaretPos
);
3130 OnCaretMove(MOVERIGHT
, bShift
);
3137 if (! HasTextSelection())
3139 POINT ptCaretPos
= GetCaretPosition();
3140 if (ptCaretPos
.y
== 0 && ptCaretPos
.x
== 0)
3142 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3144 MoveCaretWordLeft();
3147 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y
))
3151 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3153 RemoveSelectedText();
3159 if (! HasTextSelection())
3163 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3164 MoveCaretWordRight();
3165 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3169 if (! MoveCaretRight())
3171 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3173 m_ptSelectionViewPosStart
= GetCaretViewPosition();
3176 RemoveSelectedText();
3180 m_bInsertMode
= !m_bInsertMode
;
3184 if (bControl
&& m_pwndRight
)
3186 int nFirstViewLine
= 0;
3187 int nLastViewLine
= 0;
3188 if (GetViewSelection(nFirstViewLine
, nLastViewLine
))
3189 m_pwndRight
->MarkBlock(!m_pwndRight
->GetViewMarked(nFirstViewLine
));
3193 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
3196 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
3198 const int nClickedLine
= GetButtonEventLineIndex(point
);
3199 m_nLDownLine
= nClickedLine
;
3200 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
3203 ptCaretPos
.y
= nClickedLine
;
3204 int xpos2
= CalcColFromPoint(point
.x
, nClickedLine
);
3205 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, xpos2
, false);
3206 SetCaretAndGoalPosition(ptCaretPos
);
3208 if (nFlags
& MK_SHIFT
)
3209 AdjustSelection(MOVERIGHT
);
3213 SetupAllSelection(ptCaretPos
.y
, ptCaretPos
.y
);
3214 if (point
.x
< GetMarginWidth())
3216 // select the whole line
3217 m_ptSelectionViewPosStart
= m_ptSelectionViewPosEnd
= GetCaretViewPosition();
3218 m_ptSelectionViewPosStart
.x
= 0;
3219 m_ptSelectionViewPosEnd
.x
= GetViewLineLength(m_ptSelectionViewPosEnd
.y
);
3223 UpdateViewsCaretPosition();
3227 CView::OnLButtonDown(nFlags
, point
);
3230 CBaseView::ECharGroup
CBaseView::GetCharGroup(wchar_t zChar
) const
3232 if (zChar
== ' ' || zChar
== '\t' )
3234 return CHG_WHITESPACE
;
3240 if (m_sWordSeparators
.Find(zChar
) >= 0)
3242 return CHG_WORDSEPARATOR
;
3244 return CHG_WORDLETTER
;
3247 void CBaseView::OnLButtonDblClk(UINT nFlags
, CPoint point
)
3249 if (m_pViewData
== 0) {
3250 CView::OnLButtonDblClk(nFlags
, point
);
3254 const int nClickedLine
= GetButtonEventLineIndex(point
);
3255 if ( nClickedLine
< 0)
3257 int nViewLine
= GetViewLineForScreen(nClickedLine
);
3258 if (point
.x
< GetMarginWidth()) // only if double clicked on the margin
3260 if((nViewLine
< m_pViewData
->GetCount())) // a double click on moved line scrolls to corresponding line
3262 if (m_pViewData
->GetMovedIndex(nViewLine
)>=0)
3264 int movedindex
= m_pViewData
->GetMovedIndex(nViewLine
);
3265 int screenLine
= FindViewLineNumber(movedindex
);
3266 int nTop
= screenLine
- GetScreenLines()/2;
3269 ScrollAllToLine(nTop
);
3270 // find and select the whole moved block
3271 int startSel
= movedindex
;
3272 int endSel
= movedindex
;
3273 while ((startSel
> 0) && (m_pOtherViewData
->GetMovedIndex(startSel
) >= 0))
3276 while ((endSel
< GetLineCount()) && (m_pOtherViewData
->GetMovedIndex(endSel
) >= 0))
3279 m_pOtherView
->SetupSelection(startSel
, endSel
);
3280 return CView::OnLButtonDblClk(nFlags
, point
);
3284 if ((m_pMainFrame
->m_bCollapsed
)&&(m_pViewData
->GetHideState(nViewLine
) == HideState::Marker
))
3286 // a double click on a marker expands the hidden text
3288 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) != HideState::Shown
))
3290 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
3291 m_pwndLeft
->m_pViewData
->SetLineHideState(i
, HideState::Shown
);
3292 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
3293 m_pwndRight
->m_pViewData
->SetLineHideState(i
, HideState::Shown
);
3294 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
3295 m_pwndBottom
->m_pViewData
->SetLineHideState(i
, HideState::Shown
);
3298 BuildAllScreen2ViewVector();
3300 m_pwndLeft
->Invalidate();
3302 m_pwndRight
->Invalidate();
3304 m_pwndBottom
->Invalidate();
3309 ptCaretPos
.y
= nClickedLine
;
3310 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth(), false);
3311 SetCaretPosition(ptCaretPos
);
3314 POINT ptViewCarret
= GetCaretViewPosition();
3315 nViewLine
= ptViewCarret
.y
;
3316 if (nViewLine
>= GetViewCount())
3318 const CString
&sLine
= GetViewLine(nViewLine
);
3319 int nLineLength
= sLine
.GetLength();
3320 int nBasePos
= ptViewCarret
.x
;
3321 // get target char group
3322 ECharGroup eLeft
= CHG_UNKNOWN
;
3325 eLeft
= GetCharGroup(sLine
[nBasePos
-1]);
3327 ECharGroup eRight
= CHG_UNKNOWN
;
3328 if (nBasePos
< nLineLength
)
3330 eRight
= GetCharGroup(sLine
[nBasePos
]);
3332 ECharGroup eTarget
= max(eRight
, eLeft
);
3334 int nLeft
= nBasePos
;
3335 while (nLeft
> 0 && GetCharGroup(sLine
[nLeft
-1]) == eTarget
)
3340 int nRight
= nBasePos
;
3341 while (nRight
< nLineLength
&& GetCharGroup(sLine
[nRight
]) == eTarget
)
3346 m_ptSelectionViewPosStart
.x
= nLeft
;
3347 m_ptSelectionViewPosStart
.y
= nViewLine
;
3348 m_ptSelectionViewPosEnd
.x
= nRight
;
3349 m_ptSelectionViewPosEnd
.y
= nViewLine
;
3350 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
3351 SetupAllViewSelection(nViewLine
, nViewLine
);
3353 ptCaretPos
= ConvertViewPosToScreen(m_ptSelectionViewPosEnd
);
3354 UpdateViewsCaretPosition();
3358 m_sPreviousMarkedWord
= m_sMarkedWord
; // store marked word to recall in case of triple click
3359 int nMarkWidth
= max(nRight
- nLeft
, 0);
3360 m_sMarkedWord
= sLine
.Mid(m_ptSelectionViewPosStart
.x
, nMarkWidth
).Trim();
3361 if (m_sMarkedWord
.Compare(m_sPreviousMarkedWord
) == 0)
3363 m_sMarkedWord
.Empty();
3366 if (GetKeyState(VK_CONTROL
) & 0x8000)
3368 // if the ctrl key is pressed, only
3369 // mark the words in this view
3370 SetMarkedWord(m_sMarkedWord
);
3375 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3377 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3379 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3384 m_pwndLocator
->Invalidate();
3387 CView::OnLButtonDblClk(nFlags
, point
);
3390 void CBaseView::OnLButtonTrippleClick( UINT
/*nFlags*/, CPoint point
)
3392 const int nClickedLine
= GetButtonEventLineIndex(point
);
3393 if (((point
.y
- (GetLineHeight() + HEADERHEIGHT
)) / GetLineHeight()) <= 0)
3395 if (!m_sConvertedFilePath
.IsEmpty() && (GetKeyState(VK_CONTROL
)&0x8000))
3397 PCIDLIST_ABSOLUTE __unaligned pidl
= ILCreateFromPath(static_cast<LPCWSTR
>(m_sConvertedFilePath
));
3400 SHOpenFolderAndSelectItems(pidl
,0,0,0);
3401 CoTaskMemFree((LPVOID
)pidl
);
3407 ptCaretPos
.y
= nClickedLine
;
3408 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth(), false);
3409 SetCaretAndGoalPosition(ptCaretPos
);
3410 m_sMarkedWord
= m_sPreviousMarkedWord
; // recall previous Marked word
3412 m_pwndLeft
->SetMarkedWord(m_sMarkedWord
);
3414 m_pwndRight
->SetMarkedWord(m_sMarkedWord
);
3416 m_pwndBottom
->SetMarkedWord(m_sMarkedWord
);
3418 m_ptSelectionViewPosStart
.x
= 0;
3419 m_ptSelectionViewPosStart
.y
= nClickedLine
;
3420 m_ptSelectionViewPosEnd
.x
= GetLineLength(nClickedLine
);
3421 m_ptSelectionViewPosEnd
.y
= nClickedLine
;
3422 SetupSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
3423 UpdateViewsCaretPosition();
3426 m_pwndLocator
->Invalidate();
3429 void CBaseView::OnEditCopy()
3431 CString sCopyData
= GetSelectedText();
3433 if (!sCopyData
.IsEmpty())
3435 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
3439 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
3441 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0)
3443 --m_pMainFrame
->m_nMoveMovesToIgnore
;
3444 CView::OnMouseMove(nFlags
, point
);
3447 int nMouseLine
= GetButtonEventLineIndex(point
);
3448 if (nMouseLine
< -1)
3450 m_mouseInMargin
= point
.x
< GetMarginWidth();
3452 ShowDiffLines(nMouseLine
);
3454 KillTimer(IDT_SCROLLTIMER
);
3455 if (nFlags
& MK_LBUTTON
)
3457 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3458 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3459 if (saveMouseLine
< 0)
3461 int col
= CalcColFromPoint(point
.x
, saveMouseLine
);
3462 int charIndex
= CalculateCharIndex(saveMouseLine
, col
, true);
3463 if (HasSelection() &&
3464 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
3466 POINT ptCaretPos
= {charIndex
, nMouseLine
};
3467 SetCaretAndGoalPosition(ptCaretPos
);
3468 AdjustSelection(MOVERIGHT
);
3472 if (m_nLDownLine
< 0)
3473 return; // no scrolling if the click started on the header
3474 if (nMouseLine
< m_nTopLine
)
3476 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3477 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3479 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3481 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3482 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3484 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3487 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3489 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3492 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3498 CView::OnMouseMove(nFlags
, point
);
3501 void CBaseView::OnLButtonUp(UINT nFlags
, CPoint point
)
3505 KillTimer(IDT_SCROLLTIMER
);
3507 __super::OnLButtonUp(nFlags
, point
);
3510 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
3512 if (nIDEvent
== IDT_SCROLLTIMER
)
3515 GetCursorPos(&point
);
3516 ScreenToClient(&point
);
3517 int nMouseLine
= GetButtonEventLineIndex(point
);
3518 if (nMouseLine
< -1)
3522 if (GetKeyState(VK_LBUTTON
)&0x8000)
3524 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
3525 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
3526 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth(), false);
3527 if (nMouseLine
< m_nTopLine
)
3529 ScrollAllToLine(m_nTopLine
-1, TRUE
);
3530 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3532 if (nMouseLine
>= m_nTopLine
+ GetScreenLines() - 2)
3534 ScrollAllToLine(m_nTopLine
+1, TRUE
);
3535 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3537 if (!m_pMainFrame
->m_bWrapLines
&& ((m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar
))
3540 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3542 if (!m_pMainFrame
->m_bWrapLines
&& (charIndex
>= (GetScreenChars()+m_nOffsetChar
-4)))
3545 SetTimer(IDT_SCROLLTIMER
, 20, nullptr);
3551 CView::OnTimer(nIDEvent
);
3554 void CBaseView::ShowDiffLines(int nLine
)
3556 if ((nLine
< m_nTopLine
)||(nLine
>= GetLineCount()))
3558 m_pwndLineDiffBar
->ShowLines(nLine
);
3560 m_nMouseLine
= nLine
;
3564 if ((!m_pwndRight
)||(!m_pwndLeft
))
3566 if(m_pMainFrame
->m_bOneWay
)
3569 nLine
= (nLine
> m_pwndRight
->m_Screen2View
.size() ? -1 : nLine
);
3570 nLine
= (nLine
> m_pwndLeft
->m_Screen2View
.size() ? -1 : nLine
);
3575 if (nLine
!= m_nMouseLine
)
3577 if (nLine
>= GetLineCount())
3579 m_nMouseLine
= nLine
;
3580 m_pwndLineDiffBar
->ShowLines(nLine
);
3582 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
3585 const viewdata
& CBaseView::GetEmptyLineData()
3587 static const viewdata
emptyLine(L
"", DiffState::Empty
, -1, EOL::NoEnding
, HideState::Shown
);
3591 void CBaseView::InsertViewEmptyLines(int nFirstView
, int nCount
)
3593 for (int i
= 0; i
< nCount
; i
++)
3595 InsertViewData(nFirstView
, GetEmptyLineData());
3600 void CBaseView::UpdateCaret()
3602 POINT ptCaretPos
= GetCaretPosition();
3603 ptCaretPos
.y
= std::max
<int>(std::min
<int>(ptCaretPos
.y
, GetLineCount()-1), 0);
3604 ptCaretPos
.x
= std::max
<int>(std::min
<int>(ptCaretPos
.x
, GetLineLength(ptCaretPos
.y
)), 0);
3605 SetCaretPosition(ptCaretPos
);
3607 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3610 ptCaretPos
.y
>= m_nTopLine
&&
3611 ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
3612 nCaretOffset
>= m_nOffsetChar
&&
3613 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
3615 POINT pt1
= TextToClient(ptCaretPos
);
3617 CreateSolidCaret(2, GetLineHeight());
3620 POINT pt
= { ptCaretPos
.x
+ 1, ptCaretPos
.y
};
3621 POINT pt2
= TextToClient(pt
);
3622 int width
= max(GetCharWidth(), static_cast<int>(pt2
.x
- pt1
.x
));
3623 CreateSolidCaret(width
, GetLineHeight());
3634 POINT
CBaseView::ConvertScreenPosToView(const POINT
& pt
)
3639 int nSubLine
= GetSubLineOffset(pt
.y
);
3642 for (int nScreenLine
= pt
.y
-1; nScreenLine
>= pt
.y
-nSubLine
; nScreenLine
--)
3644 ptViewPos
.x
+= GetLineChars(nScreenLine
).GetLength();
3648 ptViewPos
.y
= GetViewLineForScreen(pt
.y
);
3652 POINT
CBaseView::ConvertViewPosToScreen(const POINT
& pt
)
3655 int nViewLineLenLeft
= GetViewLineLength(pt
.y
);
3656 ptPos
.x
= min(static_cast<LONG
>(nViewLineLenLeft
), pt
.x
);
3657 ptPos
.y
= FindScreenLineForViewLine(pt
.y
);
3658 if (GetViewLineForScreen(ptPos
.y
) != pt
.y
)
3662 else if (GetSubLineOffset(ptPos
.y
) >= 0) // sublined
3664 int nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3665 while (nSubLineLength
< ptPos
.x
)
3667 ptPos
.x
-= nSubLineLength
;
3668 nViewLineLenLeft
-= nSubLineLength
;
3670 nSubLineLength
= GetLineChars(ptPos
.y
).GetLength();
3671 // particular case when only first line exist. Without below statement we
3672 // have infinite loop because nSublineLengh = 0 and ptPos.x = 1, 0 < 1
3673 if (nSubLineLength
== 0)
3679 // last pos of non last sub-line go to start of next screen line
3680 // Note: while this works correctly, it's not what a user might expect:
3681 // cursor-right when the caret is before the last char of a wrapped line
3682 // now moves the caret to the next line. But users expect the caret to
3683 // move to the right of the last char instead, and with another cursor-right
3684 // keystroke to move the caret to the next line.
3685 // Basically, this would require to handle two caret positions for the same
3686 // logical position in the line string (one on the last position of the first line,
3687 // one on the first position of the new line. For non-wrapped lines this works
3688 // because there's an 'invisible' newline char at the end of the first line.
3689 if (nSubLineLength
== ptPos
.x
&& nViewLineLenLeft
> nSubLineLength
)
3700 void CBaseView::EnsureCaretVisible()
3702 POINT ptCaretPos
= GetCaretPosition();
3703 int nCaretOffset
= CalculateActualOffset(ptCaretPos
);
3705 if (ptCaretPos
.y
< m_nTopLine
)
3706 ScrollAllToLine(ptCaretPos
.y
);
3707 int screnLines
= GetScreenLines();
3710 if (ptCaretPos
.y
>= (m_nTopLine
+screnLines
)-1)
3711 ScrollAllToLine(ptCaretPos
.y
-screnLines
+2);
3712 if (nCaretOffset
< m_nOffsetChar
)
3713 ScrollAllToChar(nCaretOffset
);
3714 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
3715 ScrollAllToChar(nCaretOffset
-GetScreenChars()+1);
3719 int CBaseView::CalculateActualOffset(const POINT
& point
)
3721 int nLineIndex
= point
.y
;
3722 int nCharIndex
= point
.x
;
3723 ASSERT(nCharIndex
>= 0);
3724 CString sLine
= GetLineChars(nLineIndex
);
3725 int nLineLength
= sLine
.GetLength();
3726 return CountExpandedChars(sLine
, min(nCharIndex
, nLineLength
));
3729 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
, bool allowEOL
)
3731 int nLength
= GetLineLength(nLineIndex
);
3732 int nSubLine
= GetSubLineOffset(nLineIndex
);
3733 if (!allowEOL
&& nSubLine
>= 0)
3735 int nViewLine
= GetViewLineForScreen(nLineIndex
);
3736 if (nViewLine
>= 0 && nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()))
3738 int nMultilineCount
= CountMultiLines(nViewLine
);
3739 if ((nMultilineCount
>0) && (nSubLine
<nMultilineCount
-1))
3745 CString Line
= GetLineChars(nLineIndex
);
3748 int nTabSize
= GetTabSize();
3749 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
3751 if (Line
.GetAt(nIndex
) == L
'\t')
3752 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
3761 * @param xpos X coordinate in CBaseView
3762 * @param lineIndex logical line index (e.g. wrap/collapse)
3764 int CBaseView::CalcColFromPoint(int xpos
, int lineIndex
)
3770 CString text
= ExpandChars(GetLineChars(lineIndex
), 0);
3771 int fit
= text
.GetLength();
3772 auto posBuffer
= std::make_unique
<int[]>(fit
);
3773 pDC
->SelectObject(GetFont()); // is this right font ?
3775 GetTextExtentExPoint(pDC
->GetSafeHdc(), text
, fit
, INT_MAX
, &fit
, posBuffer
.get(), &size
);
3777 int lower
= -1, upper
= fit
- 1;
3778 int xcheck
= xpos
- GetMarginWidth() + m_nOffsetChar
* GetCharWidth();
3781 int middle
= (upper
+ lower
+ 1) / 2;
3782 int width
= posBuffer
[middle
];
3787 } while (lower
< upper
);
3790 if (lower
< fit
- 1)
3792 int charWidth
= posBuffer
[lower
] - (lower
> 0 ? posBuffer
[lower
- 1] : 0);
3793 if (posBuffer
[lower
] - xcheck
<= charWidth
/ 2)
3799 xpos2
= (xpos
- GetMarginWidth()) / GetCharWidth() + m_nOffsetChar
;
3800 if ((xpos
% GetCharWidth()) >= (GetCharWidth()/2))
3806 POINT
CBaseView::TextToClient(const POINT
& point
)
3809 int nOffsetScreenLine
= max(0, static_cast<int>(point
.y
- m_nTopLine
));
3810 pt
.y
= nOffsetScreenLine
* GetLineHeight();
3811 pt
.x
= CalculateActualOffset(point
);
3813 int nLeft
= GetMarginWidth() - m_nOffsetChar
* GetCharWidth();
3814 CDC
* pDC
= GetDC();
3817 pDC
->SelectObject(GetFont()); // is this right font ?
3818 int nScreenLine
= nOffsetScreenLine
+ m_nTopLine
;
3819 CString sLine
= GetLineChars(nScreenLine
);
3820 ExpandChars(sLine
, 0, std::min
<int>(pt
.x
, sLine
.GetLength()), sLine
);
3821 nLeft
+= pDC
->GetTextExtent(sLine
, pt
.x
).cx
;
3824 nLeft
+= pt
.x
* GetCharWidth();
3828 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
3832 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
3834 CView::OnChar(nChar
, nRepCnt
, nFlags
);
3836 bool bControl
= !!(GetKeyState(VK_CONTROL
) & 0x8000);
3837 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
3838 bool bSkipSelectionClear
= false;
3843 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
3844 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
3849 if (!m_pViewData
) // no data - nothing to do
3852 if (nChar
== VK_F16
)
3854 // generated by a ctrl+backspace - ignore.
3856 else if (nChar
==VK_TAB
&& HasTextLineSelection())
3858 // change indentation for selected lines
3861 RemoveIndentationForSelectedBlock();
3865 AddIndentationForSelectedBlock();
3867 bSkipSelectionClear
= true;
3869 else if ((nChar
> 31)||(nChar
== VK_TAB
))
3872 RemoveSelectedText();
3873 POINT ptCaretViewPos
= GetCaretViewPosition();
3874 int nViewLine
= ptCaretViewPos
.y
;
3875 if ((nViewLine
==0)&&(GetViewCount()==0))
3876 OnChar(VK_RETURN
, 0, 0);
3878 viewdata lineData
= GetViewData(nViewLine
);
3879 if (nChar
== VK_TAB
)
3881 int indentChars
= GetIndentCharsForLine(ptCaretViewPos
.x
, nViewLine
);
3882 if (indentChars
> 0)
3884 lineData
.sLine
.Insert(ptCaretViewPos
.x
, CString(L
' ', indentChars
));
3885 charCount
= indentChars
;
3888 lineData
.sLine
.Insert(ptCaretViewPos
.x
, L
'\t');
3893 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3896 if (lineData
.sLine
.GetLength() > ptCaretViewPos
.x
)
3897 lineData
.sLine
.SetAt(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3899 lineData
.sLine
.Insert(ptCaretViewPos
.x
, static_cast<wchar_t>(nChar
));
3902 if (IsStateEmpty(lineData
.state
) || IsStateConflicted(lineData
.state
) || lineData
.state
== DiffState::IdenticalRemoved
)
3904 // if not last line set EOL
3905 for (int nCheckViewLine
= nViewLine
+1; nCheckViewLine
< GetViewCount(); nCheckViewLine
++)
3907 if (!IsViewLineEmpty(nCheckViewLine
))
3909 lineData
.ending
= m_lineendings
;
3913 // make sure previous (non empty) line have EOL set
3914 for (int nCheckViewLine
= nViewLine
-1; nCheckViewLine
> 0; nCheckViewLine
--)
3916 if (!IsViewLineEmpty(nCheckViewLine
) && GetViewState(nCheckViewLine
) != DiffState::IdenticalRemoved
)
3918 if (GetViewLineEnding(nCheckViewLine
) == EOL::NoEnding
)
3920 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
3926 lineData
.state
= DiffState::Edited
;
3927 bool bNeedRenumber
= false;
3928 if (lineData
.linenumber
== -1)
3930 lineData
.linenumber
= 0;
3931 bNeedRenumber
= true;
3933 SetViewData(nViewLine
, lineData
);
3936 BuildAllScreen2ViewVector(nViewLine
);
3939 UpdateViewLineNumbers();
3941 for (int i
= 0; i
< charCount
; ++i
)
3945 else if (nChar
== 10)
3947 int nViewLine
= GetViewLineForScreen(GetCaretPosition().y
);
3948 EOL eol
= m_pViewData
->GetLineEnding(nViewLine
);
3949 EOL newEOL
= EOL::CRLF
;
3962 if (eol
== EOL::NoEnding
|| eol
== newEOL
)
3963 // don't allow to change enter on empty line, or last text line (lines with EOL::NoEnding)
3964 // to add EOL on newly edited empty line hit enter
3965 // don't store into UNDO if no change happened
3966 // and don't mark file as modified
3968 AddUndoViewLine(nViewLine
);
3969 m_pViewData
->SetLineEnding(nViewLine
, newEOL
);
3970 m_pViewData
->SetState(nViewLine
, DiffState::Edited
);
3973 else if ((nChar
== VK_RETURN
) && !bControl
)
3975 // insert a new, fresh and empty line below the cursor
3976 RemoveSelectedText();
3978 CUndo::GetInstance().BeginGrouping();
3980 POINT ptCaretViewPos
= GetCaretViewPosition();
3981 int nViewLine
= ptCaretViewPos
.y
;
3982 int nLeft
= ptCaretViewPos
.x
;
3983 CString sLine
= GetViewLineChars(nViewLine
);
3984 CString sLineLeft
= sLine
.Left(nLeft
);
3985 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
3986 EOL eOriginalEnding
= EOL::AutoLine
;
3987 if (m_pViewData
->GetCount() > nViewLine
)
3988 eOriginalEnding
= GetViewLineEnding(nViewLine
);
3990 if (!sLineRight
.IsEmpty() || (eOriginalEnding
!=m_lineendings
))
3992 viewdata
newFirstLine(sLineLeft
, DiffState::Edited
, 1, m_lineendings
, HideState::Shown
);
3993 SetViewData(nViewLine
, newFirstLine
);
3996 int nInsertLine
= (m_pViewData
->GetCount()==0) ? 0 : nViewLine
+ 1;
3997 viewdata
newLastLine(sLineRight
, DiffState::Edited
, 1, eOriginalEnding
, HideState::Shown
);
3998 InsertViewData(nInsertLine
, newLastLine
);
4002 // adds new line everywhere except me
4003 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!=this)
4005 m_pwndLeft
->InsertViewEmptyLines(nInsertLine
, 1);
4007 if (IsViewGood(m_pwndRight
) && m_pwndRight
!=this)
4009 m_pwndRight
->InsertViewEmptyLines(nInsertLine
, 1);
4011 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!=this)
4013 m_pwndBottom
->InsertViewEmptyLines(nInsertLine
, 1);
4017 UpdateViewLineNumbers();
4019 CUndo::GetInstance().EndGrouping();
4021 BuildAllScreen2ViewVector();
4022 // move the cursor to the new line
4023 ptCaretViewPos
= SetupPoint(0, nViewLine
+1);
4024 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4027 return; // Unknown control character -- ignore it.
4028 if (!bSkipSelectionClear
)
4030 EnsureCaretVisible();
4035 void CBaseView::AddUndoViewLine(int nViewLine
, bool bAddEmptyLine
)
4038 m_AllState
.left
.AddViewLineFromView(m_pwndLeft
, nViewLine
, bAddEmptyLine
);
4039 m_AllState
.right
.AddViewLineFromView(m_pwndRight
, nViewLine
, bAddEmptyLine
);
4040 m_AllState
.bottom
.AddViewLineFromView(m_pwndBottom
, nViewLine
, bAddEmptyLine
);
4043 RecalcAllVertScrollBars();
4047 void CBaseView::AddEmptyViewLine(int nViewLineIndex
)
4051 int viewLine
= nViewLineIndex
;
4052 EOL ending
= m_pViewData
->GetLineEnding(viewLine
);
4053 if (ending
== EOL::NoEnding
)
4055 ending
= m_lineendings
;
4057 viewdata
newLine(L
"", DiffState::Edited
, -1, ending
, HideState::Shown
);
4058 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
4060 CString sPartLine
= GetViewLineChars(nViewLineIndex
);
4061 int nPosx
= GetCaretPosition().x
; // should be view pos ?
4062 m_pViewData
->SetLine(viewLine
, sPartLine
.Left(nPosx
));
4063 sPartLine
= sPartLine
.Mid(nPosx
);
4064 newLine
.sLine
= sPartLine
;
4066 m_pViewData
->InsertData(viewLine
+1, newLine
);
4067 BuildAllScreen2ViewVector();
4070 void CBaseView::RemoveSelectedText()
4074 if (!HasTextSelection())
4077 // fix selection if starts or ends on empty line
4078 SetCaretViewPosition(m_ptSelectionViewPosEnd
);
4079 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4082 m_ptSelectionViewPosEnd
= GetCaretViewPosition();
4083 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4084 while (IsViewLineEmpty(GetCaretViewPosition().y
) && MoveCaretRight())
4087 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4088 if (!HasTextSelection())
4094 // We want to undo the insertion in a single step.
4096 CUndo::GetInstance().BeginGrouping();
4098 // combine first and last line
4099 viewdata oFirstLine
= GetViewData(m_ptSelectionViewPosStart
.y
);
4100 viewdata oLastLine
= GetViewData(m_ptSelectionViewPosEnd
.y
);
4101 oFirstLine
.sLine
= oFirstLine
.sLine
.Left(m_ptSelectionViewPosStart
.x
) + oLastLine
.sLine
.Mid(m_ptSelectionViewPosEnd
.x
);
4102 oFirstLine
.ending
= (oLastLine
.ending
!= EOL::NoEnding
|| m_ptSelectionViewPosEnd
.y
== (GetLineCount() - 1)) ? oLastLine
.ending
: oFirstLine
.ending
;
4103 oFirstLine
.state
= DiffState::Edited
;
4104 SetViewData(m_ptSelectionViewPosStart
.y
, oFirstLine
);
4106 // clean up middle lines if any
4107 if (m_ptSelectionViewPosStart
.y
!= m_ptSelectionViewPosEnd
.y
)
4109 viewdata oEmptyLine
= GetEmptyLineData();
4110 for (int nViewLine
= m_ptSelectionViewPosStart
.y
+1; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
4112 SetViewData(nViewLine
, oEmptyLine
);
4116 if (CleanEmptyLines())
4118 BuildAllScreen2ViewVector(); // schedule full rebuild
4121 UpdateViewLineNumbers();
4124 SetModified(); //TODO set modified only if real data was changed
4126 CUndo::GetInstance().EndGrouping();
4128 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4129 SetCaretViewPosition(m_ptSelectionViewPosStart
);
4133 EnsureCaretVisible();
4137 void CBaseView::PasteText()
4139 CClipboardHelper clipboardHelper
;
4140 if (!clipboardHelper
.Open(nullptr))
4143 CString sClipboardText
;
4144 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
4147 LPCSTR lpstr
= static_cast<LPCSTR
>(GlobalLock(hglb
));
4148 sClipboardText
= CString(lpstr
);
4151 hglb
= GetClipboardData(CF_UNICODETEXT
);
4154 LPCWSTR lpstr
= static_cast<LPCWSTR
>(GlobalLock(hglb
));
4155 sClipboardText
= lpstr
;
4159 if (sClipboardText
.IsEmpty())
4162 sClipboardText
.Replace(L
"\r\n", L
"\r");
4163 sClipboardText
.Replace('\n', '\r');
4165 InsertText(sClipboardText
);
4168 void CBaseView::OnCaretDown()
4170 POINT ptCaretPos
= GetCaretPosition();
4171 int nLine
= ptCaretPos
.y
;
4172 int nNextLine
= nLine
+ 1;
4173 if (nNextLine
>= GetLineCount()) // already at last line
4178 POINT ptCaretViewPos
= GetCaretViewPosition();
4179 int nViewLine
= ptCaretViewPos
.y
;
4180 int nNextViewLine
= GetViewLineForScreen(nNextLine
);
4181 if (!((nNextViewLine
== nViewLine
) && (GetSubLineOffset(nNextLine
)<CountMultiLines(nNextViewLine
)))) // not on same view line
4183 // find next suitable screen line
4184 while ((nNextViewLine
== nViewLine
) || IsViewLineHidden(nNextViewLine
))
4187 if (nNextLine
>= GetLineCount())
4191 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4194 ptCaretPos
.y
= nNextLine
;
4195 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
, false);
4196 SetCaretPosition(ptCaretPos
);
4197 OnCaretMove(MOVELEFT
);
4198 ShowDiffLines(ptCaretPos
.y
);
4201 bool CBaseView::MoveCaretLeft()
4203 POINT ptCaretViewPos
= GetCaretViewPosition();
4205 //int nViewLine = ptCaretViewPos.y;
4206 if (ptCaretViewPos
.x
== 0)
4208 int nPrevLine
= GetCaretPosition().y
;
4216 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4217 } while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
));
4218 ptCaretViewPos
= ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine
), nPrevLine
));
4219 ShowDiffLines(nPrevLine
);
4224 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4228 bool CBaseView::MoveCaretRight()
4230 POINT ptCaretViewPos
= GetCaretViewPosition();
4232 int nViewLine
= ptCaretViewPos
.y
;
4233 int nViewLineLen
= GetViewLineLength(nViewLine
);
4234 if (ptCaretViewPos
.x
>= nViewLineLen
)
4236 int nNextLine
= GetCaretPosition().y
;
4240 if (nNextLine
>= GetLineCount())
4244 nNextViewLine
= GetViewLineForScreen(nNextLine
);
4245 } while (nNextViewLine
== nViewLine
|| IsViewLineHidden(nNextViewLine
));
4246 ptCaretViewPos
.y
= nNextViewLine
;
4247 ptCaretViewPos
.x
= 0;
4248 ShowDiffLines(nNextLine
);
4253 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4257 void CBaseView::UpdateGoalPos()
4259 m_nCaretGoalPos
= CalculateActualOffset(GetCaretPosition());
4262 void CBaseView::OnCaretLeft()
4265 OnCaretMove(MOVELEFT
);
4268 void CBaseView::OnCaretRight()
4271 OnCaretMove(MOVERIGHT
);
4274 void CBaseView::OnCaretUp()
4276 POINT ptCaretPos
= GetCaretPosition();
4277 int nLine
= ptCaretPos
.y
;
4278 if (nLine
<= 0) // already at first line
4282 int nPrevLine
= nLine
- 1;
4284 POINT ptCaretViewPos
= GetCaretViewPosition();
4285 int nViewLine
= ptCaretViewPos
.y
;
4286 int nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4287 if (nPrevViewLine
!= nViewLine
) // not on same view line
4289 // find previous suitable screen line
4290 while ((GetSubLineOffset(nPrevLine
) >= CountMultiLines(nPrevViewLine
)) || IsViewLineHidden(nPrevViewLine
))
4297 nPrevViewLine
= GetViewLineForScreen(nPrevLine
);
4300 ptCaretPos
.y
= nPrevLine
;
4301 ptCaretPos
.x
= CalculateCharIndex(ptCaretPos
.y
, m_nCaretGoalPos
, false);
4302 SetCaretPosition(ptCaretPos
);
4303 OnCaretMove(MOVELEFT
);
4304 ShowDiffLines(ptCaretPos
.y
);
4307 bool CBaseView::IsWordSeparator(const wchar_t ch
) const
4309 switch (GetCharGroup(ch
))
4312 case CHG_WHITESPACE
:
4313 case CHG_WORDSEPARATOR
:
4319 bool CBaseView::IsCaretAtWordBoundary()
4321 POINT ptViewCaret
= GetCaretViewPosition();
4322 CString line
= GetViewLineChars(ptViewCaret
.y
);
4324 return false; // no boundary at the empty lines
4325 if (ptViewCaret
.x
== 0)
4326 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
));
4327 if (ptViewCaret
.x
>= GetViewLineLength(ptViewCaret
.y
))
4328 return !IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4330 IsWordSeparator(line
.GetAt(ptViewCaret
.x
)) !=
4331 IsWordSeparator(line
.GetAt(ptViewCaret
.x
- 1));
4334 void CBaseView::UpdateViewsCaretPosition()
4336 POINT ptCaretPos
= GetCaretPosition();
4337 if (m_pwndBottom
&& m_pwndBottom
!=this)
4338 m_pwndBottom
->UpdateCaretPosition(ptCaretPos
);
4339 if (m_pwndLeft
&& m_pwndLeft
!=this)
4340 m_pwndLeft
->UpdateCaretPosition(ptCaretPos
);
4341 if (m_pwndRight
&& m_pwndRight
!=this)
4342 m_pwndRight
->UpdateCaretPosition(ptCaretPos
);
4345 void CBaseView::OnCaretWordleft()
4347 MoveCaretWordLeft();
4348 OnCaretMove(MOVELEFT
);
4351 void CBaseView::OnCaretWordright()
4353 MoveCaretWordRight();
4354 OnCaretMove(MOVERIGHT
);
4357 void CBaseView::MoveCaretWordLeft()
4359 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4364 void CBaseView::MoveCaretWordRight()
4366 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4371 void CBaseView::ClearCurrentSelection()
4373 m_ptSelectionViewPosStart
= GetCaretViewPosition();
4374 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosStart
;
4375 m_ptSelectionViewPosOrigin
= m_ptSelectionViewPosStart
;
4376 m_nSelViewBlockStart
= -1;
4377 m_nSelViewBlockEnd
= -1;
4381 void CBaseView::ClearSelection()
4384 m_pwndLeft
->ClearCurrentSelection();
4386 m_pwndRight
->ClearCurrentSelection();
4388 m_pwndBottom
->ClearCurrentSelection();
4391 void CBaseView::AdjustSelection(bool bMoveLeft
)
4393 POINT ptCaretViewPos
= GetCaretViewPosition();
4394 if (ArePointsSame(m_ptSelectionViewPosOrigin
, SetupPoint(-1, -1)))
4396 // select all have been used recently update origin
4397 m_ptSelectionViewPosOrigin
= bMoveLeft
? m_ptSelectionViewPosEnd
: m_ptSelectionViewPosStart
;
4399 if ((ptCaretViewPos
.y
< m_ptSelectionViewPosOrigin
.y
) ||
4400 (ptCaretViewPos
.y
== m_ptSelectionViewPosOrigin
.y
&& ptCaretViewPos
.x
<= m_ptSelectionViewPosOrigin
.x
))
4402 m_ptSelectionViewPosStart
= ptCaretViewPos
;
4403 m_ptSelectionViewPosEnd
= m_ptSelectionViewPosOrigin
;
4407 m_ptSelectionViewPosStart
= m_ptSelectionViewPosOrigin
;
4408 m_ptSelectionViewPosEnd
= ptCaretViewPos
;
4411 SetupAllViewSelection(m_ptSelectionViewPosStart
.y
, m_ptSelectionViewPosEnd
.y
);
4416 void CBaseView::OnEditCut()
4421 RemoveSelectedText();
4425 void CBaseView::OnEditPaste()
4429 CUndo::GetInstance().BeginGrouping();
4430 RemoveSelectedText();
4432 CUndo::GetInstance().EndGrouping();
4436 void CBaseView::DeleteFonts()
4438 for (int i
=0; i
<fontsCount
; i
++)
4442 m_apFonts
[i
]->DeleteObject();
4443 delete m_apFonts
[i
];
4444 m_apFonts
[i
] = nullptr;
4449 void CBaseView::OnCaretMove(bool bMoveLeft
)
4451 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
4452 OnCaretMove(bMoveLeft
, bShift
);
4455 void CBaseView::OnCaretMove(bool bMoveLeft
, bool isShiftPressed
)
4458 AdjustSelection(bMoveLeft
);
4461 EnsureCaretVisible();
4465 void CBaseView::AddContextItems(CIconMenu
& popup
, DiffState
/*state*/)
4467 AddCutCopyAndPaste(popup
);
4470 void CBaseView::AddCutCopyAndPaste(CIconMenu
& popup
)
4472 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4474 temp
.LoadString(IDS_EDIT_COPY
);
4475 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_COPY
, temp
);
4478 temp
.LoadString(IDS_EDIT_CUT
);
4479 popup
.AppendMenu(MF_STRING
| (HasTextSelection() ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_CUT
, temp
);
4480 temp
.LoadString(IDS_EDIT_PASTE
);
4481 popup
.AppendMenu(MF_STRING
| (CAppUtils::HasClipboardFormat(CF_UNICODETEXT
)||CAppUtils::HasClipboardFormat(CF_TEXT
) ? MF_ENABLED
: MF_DISABLED
|MF_GRAYED
), ID_EDIT_PASTE
, temp
);
4482 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
4486 void CBaseView::CompensateForKeyboard(CPoint
& point
)
4488 // if the context menu is invoked through the keyboard, we have to use
4489 // a calculated position on where to anchor the menu on
4490 if (ArePointsSame(point
, SetupPoint(-1, -1)))
4493 GetWindowRect(&rect
);
4494 point
= rect
.CenterPoint();
4498 void CBaseView::ReleaseBitmap()
4502 m_pCacheBitmap
->DeleteObject();
4503 delete m_pCacheBitmap
;
4504 m_pCacheBitmap
= nullptr;
4508 void CBaseView::BuildMarkedWordArray()
4510 int lineCount
= GetLineCount();
4511 m_arMarkedWordLines
.clear();
4512 m_arMarkedWordLines
.reserve(lineCount
);
4513 bool bDoit
= !m_sMarkedWord
.IsEmpty();
4514 m_MarkedWordCount
= 0;
4515 for (int i
= 0; i
< lineCount
; ++i
)
4519 CString line
= GetLineChars(i
);
4521 if (!line
.IsEmpty())
4524 int nMarkStart
= -1;
4525 while ((nMarkStart
= line
.Find(m_sMarkedWord
, ++nMarkStart
)) >= 0)
4527 int nMarkEnd
= nMarkStart
+ m_sMarkedWord
.GetLength();
4528 ECharGroup eLeft
= GetCharGroup(line
, nMarkStart
- 1);
4529 ECharGroup eStart
= GetCharGroup(line
, nMarkStart
);
4530 if (eLeft
!= eStart
)
4532 ECharGroup eRight
= GetCharGroup(line
, nMarkEnd
);
4533 ECharGroup eEnd
= GetCharGroup(line
, nMarkEnd
- 1);
4537 ++m_MarkedWordCount
;
4542 m_arMarkedWordLines
.push_back(found
);
4545 m_arMarkedWordLines
.push_back(0);
4548 m_arMarkedWordLines
.push_back(0);
4552 void CBaseView::BuildFindStringArray()
4554 int lineCount
= GetLineCount();
4555 m_arFindStringLines
.clear();
4556 m_arFindStringLines
.reserve(lineCount
);
4557 bool bDoit
= !m_sFindText
.IsEmpty();
4560 for (int i
= 0; i
< lineCount
; ++i
)
4564 CString line
= GetLineChars(i
);
4566 if (!line
.IsEmpty())
4568 switch (m_pViewData
->GetState(GetViewLineForScreen(i
)))
4570 case DiffState::Empty
:
4571 m_arFindStringLines
.push_back(0);
4573 case DiffState::Unknown
:
4574 case DiffState::Normal
:
4575 case DiffState::FilteredDiff
:
4578 m_arFindStringLines
.push_back(0);
4581 case DiffState::Removed
:
4582 case DiffState::RemovedWhitespace
:
4583 case DiffState::Added
:
4584 case DiffState::AddedWhitespace
:
4585 case DiffState::Whitespace
:
4586 case DiffState::WhitespaceDiff
:
4587 case DiffState::Conflicted
:
4588 case DiffState::Conflicted_Ignored
:
4589 case DiffState::ConflictAdded
:
4590 case DiffState::ConflictEmpty
:
4591 case DiffState::ConflictResolved
:
4592 case DiffState::IdenticalRemoved
:
4593 case DiffState::IdenticalAdded
:
4594 case DiffState::TheirsRemoved
:
4595 case DiffState::TheirsAdded
:
4596 case DiffState::YoursRemoved
:
4597 case DiffState::YoursAdded
:
4598 case DiffState::Edited
:
4601 line
= line
.MakeLower();
4605 while (StringFound(line
, SearchNext
, s
, e
))
4611 m_arFindStringLines
.push_back(match
);
4615 m_arFindStringLines
.push_back(0);
4619 m_arFindStringLines
.push_back(0);
4622 m_arFindStringLines
.push_back(0);
4627 bool CBaseView::GetInlineDiffPositions(int nViewLine
, std::vector
<inlineDiffPos
>& positions
)
4629 if (!m_bShowInlineDiff
)
4631 if (m_pwndBottom
&& !(m_pwndBottom
->IsHidden()))
4634 if (!m_pViewData
|| m_pViewData
->GetCount() <= nViewLine
)
4636 const CString
&sLine
= m_pViewData
->GetLine(nViewLine
);
4637 if (sLine
.IsEmpty())
4641 if (!m_pOtherViewData
)
4644 const CString
&sDiffLine
= m_pOtherViewData
->GetLine(nViewLine
);
4645 if (sDiffLine
.IsEmpty())
4648 svn_diff_t
* diff
= nullptr;
4649 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
4650 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
4651 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
4652 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
4655 size_t lineoffset
= 0;
4656 size_t position
= 0;
4659 if (this == m_pwndRight
)
4661 apr_off_t nTmp
= diff
->modified_length
;
4662 diff
->modified_length
= diff
->original_length
;
4663 diff
->original_length
= nTmp
;
4665 nTmp
= diff
->modified_start
;
4666 diff
->modified_start
= diff
->original_start
;
4667 diff
->original_start
= nTmp
;
4669 apr_off_t len
= diff
->original_length
;
4670 size_t oldpos
= position
;
4672 for (apr_off_t i
= 0; i
< len
; ++i
)
4674 position
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
4678 if (diff
->type
== svn_diff__type_diff_modified
)
4683 positions
.push_back(p
);
4689 return !positions
.empty();
4692 void CBaseView::OnNavigateNextinlinediff()
4695 if (GetNextInlineDiff(nX
))
4697 POINT ptCaretViewPos
= GetCaretViewPosition();
4698 ptCaretViewPos
.x
= nX
;
4699 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4700 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4701 EnsureCaretVisible();
4705 void CBaseView::OnNavigatePrevinlinediff()
4708 if (GetPrevInlineDiff(nX
))
4710 POINT ptCaretViewPos
= GetCaretViewPosition();
4711 ptCaretViewPos
.x
= nX
;
4712 SetCaretAndGoalViewPosition(ptCaretViewPos
);
4713 m_ptSelectionViewPosOrigin
= ptCaretViewPos
;
4714 EnsureCaretVisible();
4718 bool CBaseView::HasNextInlineDiff()
4721 return GetNextInlineDiff(nPos
);
4724 bool CBaseView::GetNextInlineDiff(int & nPos
)
4726 POINT ptCaretViewPos
= GetCaretViewPosition();
4727 std::vector
<inlineDiffPos
> positions
;
4728 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4730 for (auto it
= positions
.cbegin(); it
!= positions
.cend(); ++it
)
4732 if (it
->start
> ptCaretViewPos
.x
)
4734 nPos
= static_cast<LONG
>(it
->start
);
4737 if (it
->end
> ptCaretViewPos
.x
)
4739 nPos
= static_cast<LONG
>(it
->end
);
4747 bool CBaseView::HasPrevInlineDiff()
4750 return GetPrevInlineDiff(nPos
);
4753 bool CBaseView::GetPrevInlineDiff(int & nPos
)
4755 POINT ptCaretViewPos
= GetCaretViewPosition();
4756 std::vector
<inlineDiffPos
> positions
;
4757 if (GetInlineDiffPositions(ptCaretViewPos
.y
, positions
))
4759 for (auto it
= positions
.crbegin(); it
!= positions
.crend(); ++it
)
4761 if ( it
->end
< ptCaretViewPos
.x
)
4763 nPos
= static_cast<LONG
>(it
->end
);
4766 if ( it
->start
< ptCaretViewPos
.x
)
4768 nPos
= static_cast<LONG
>(it
->start
);
4776 CBaseView
* CBaseView::GetFirstGoodView()
4778 if (IsViewGood(m_pwndLeft
))
4780 if (IsViewGood(m_pwndRight
))
4782 if (IsViewGood(m_pwndBottom
))
4783 return m_pwndBottom
;
4787 void CBaseView::BuildAllScreen2ViewVector()
4789 CBaseView
* p_pwndView
= GetFirstGoodView();
4792 m_Screen2View
.ScheduleFullRebuild(p_pwndView
->m_pViewData
);
4796 void CBaseView::BuildAllScreen2ViewVector(int nViewLine
)
4798 BuildAllScreen2ViewVector(nViewLine
, nViewLine
);
4801 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine
, int nLastViewLine
)
4803 CBaseView
* p_pwndView
= GetFirstGoodView();
4806 m_Screen2View
.ScheduleRangeRebuild(p_pwndView
->m_pViewData
, nFirstViewLine
, nLastViewLine
);
4810 void CBaseView::UpdateViewLineNumbers()
4812 int nLineNumber
= 0;
4813 int nViewLineCount
= GetViewCount();
4814 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; nViewLine
++)
4816 int oldLine
= GetViewLineNumber(nViewLine
);
4818 SetViewLineNumber(nViewLine
, nLineNumber
++);
4823 int CBaseView::CleanEmptyLines()
4825 int nRemovedCount
= 0;
4826 int nViewLineCount
= GetViewCount();
4827 bool bCheckLeft
= IsViewGood(m_pwndLeft
);
4828 bool bCheckRight
= IsViewGood(m_pwndRight
);
4829 bool bCheckBottom
= IsViewGood(m_pwndBottom
);
4830 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; )
4832 bool bAllEmpty
= true;
4833 bAllEmpty
&= !bCheckLeft
|| IsStateEmpty(m_pwndLeft
->GetViewState(nViewLine
));
4834 bAllEmpty
&= !bCheckRight
|| IsStateEmpty(m_pwndRight
->GetViewState(nViewLine
));
4835 bAllEmpty
&= !bCheckBottom
|| IsStateEmpty(m_pwndBottom
->GetViewState(nViewLine
));
4840 m_pwndLeft
->RemoveViewData(nViewLine
);
4844 m_pwndRight
->RemoveViewData(nViewLine
);
4848 m_pwndBottom
->RemoveViewData(nViewLine
);
4850 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4860 return nRemovedCount
;
4863 int CBaseView::FindScreenLineForViewLine( int viewLine
)
4865 return m_Screen2View
.FindScreenLineForViewLine(viewLine
);
4868 int CBaseView::CountMultiLines( int nViewLine
)
4870 if (m_ScreenedViewLine
.empty())
4871 return 0; // in case the view is completely empty
4873 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4875 if (m_ScreenedViewLine
[nViewLine
].bSublinesSet
)
4877 return static_cast<int>(m_ScreenedViewLine
[nViewLine
].SubLines
.size());
4880 auto multiLines
= CStringUtils::WordWrap(m_pViewData
->GetLine(nViewLine
), GetScreenChars() - 1, GetTabSize());
4882 TScreenedViewLine oScreenedLine
;
4883 bool subLinesSet
= true;
4884 if (m_pMainFrame
->m_bWrapLines
)
4887 CFont
* pOldFont
= pDC
->SelectObject(GetFont());
4889 for (const auto& line
: multiLines
)
4891 if (line
.GetLength())
4893 // we use the 'X' char to determine the char width,
4894 // but e.g. chinese chars are much wider. To make sure
4895 // that we wrap correctly, we calculate the average char width
4896 // here by using the real line text
4897 const CSize szCharExt
= pDC
->GetTextExtent(line
);
4898 if (szCharExt
.cx
/ line
.GetLength() > m_nCharWidth
)
4900 m_nCharWidth
= szCharExt
.cx
/ line
.GetLength();
4901 subLinesSet
= false;
4904 oScreenedLine
.SubLines
.push_back(line
);
4906 pDC
->SelectObject(pOldFont
);
4911 for (const auto& line
: multiLines
)
4913 oScreenedLine
.SubLines
.push_back(line
);
4917 oScreenedLine
.bSublinesSet
= subLinesSet
;
4918 m_ScreenedViewLine
[nViewLine
] = oScreenedLine
;
4920 return CountMultiLines(nViewLine
);
4923 /// prepare inline diff cache
4924 LineColors
& CBaseView::GetLineColors(int nViewLine
)
4926 ASSERT(nViewLine
< static_cast<int>(m_ScreenedViewLine
.size()));
4928 if (m_bWhitespaceInlineDiffs
)
4930 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
)
4931 return m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
;
4935 if (m_ScreenedViewLine
[nViewLine
].bLineColorsSet
)
4936 return m_ScreenedViewLine
[nViewLine
].lineColors
;
4939 LineColors oLineColors
;
4940 // set main line color
4941 COLORREF crBkgnd
, crText
;
4942 DiffState diffState
= m_pViewData
->GetState(nViewLine
);
4943 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
4944 oLineColors
.SetColor(0, crText
, crBkgnd
);
4947 if (!m_bShowInlineDiff
)
4950 if (((diffState
== DiffState::Normal
) || (diffState
== DiffState::FilteredDiff
)) && (!m_bWhitespaceInlineDiffs
))
4953 CString sLine
= GetViewLineChars(nViewLine
);
4954 if (sLine
.IsEmpty())
4961 case DiffState::Added
:
4963 if ((nViewLine
> 0) && (m_pViewData
->GetState(nViewLine
- 1) == DiffState::Removed
))
4964 sDiffLine
= GetViewLineChars(nViewLine
- 1);
4967 case DiffState::Removed
:
4969 if (((nViewLine
+ 1) < m_pViewData
->GetCount()) && (m_pViewData
->GetState(nViewLine
+ 1) == DiffState::Added
))
4970 sDiffLine
= GetViewLineChars(nViewLine
+ 1);
4976 sDiffLine
= m_pOtherView
->GetViewLineChars(nViewLine
);
4977 if (sDiffLine
.IsEmpty())
4980 svn_diff_t
* diff
= nullptr;
4981 if (sLine
.GetLength() > static_cast<int>(m_nInlineDiffMaxLineLength
))
4983 auto pLine1
= (this == m_pwndLeft
) ? &sLine
: &sDiffLine
;
4984 auto pLine2
= (this == m_pwndLeft
) ? &sDiffLine
: &sLine
;
4985 m_svnlinediff
.Diff(&diff
, *pLine1
, pLine1
->GetLength(), *pLine2
, pLine2
->GetLength(), m_bInlineWordDiff
);
4986 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
) || !diff
->next
)
4990 int nTextStartOffset
= 0;
4991 std::map
<int, COLORREF
> removedPositions
;
4994 if (this == m_pwndRight
)
4996 apr_off_t nTmp
= diff
->modified_length
;
4997 diff
->modified_length
= diff
->original_length
;
4998 diff
->original_length
= nTmp
;
5000 nTmp
= diff
->modified_start
;
5001 diff
->modified_start
= diff
->original_start
;
5002 diff
->original_start
= nTmp
;
5004 apr_off_t len
= diff
->original_length
;
5006 size_t nTextLength
= 0;
5007 for (int i
= 0; i
< len
; ++i
)
5009 nTextLength
+= (this == m_pwndRight
) ? m_svnlinediff
.m_line2tokens
[lineoffset
].size() : m_svnlinediff
.m_line1tokens
[lineoffset
].size();
5012 bool bInlineDiff
= (diff
->type
== svn_diff__type_diff_modified
);
5014 CDiffColors::GetInstance().GetColors(diffState
, CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark(), crBkgnd
, crText
);
5015 if ((m_bShowInlineDiff
)&&(bInlineDiff
))
5017 crBkgnd
= InlineViewLineDiffColor(nViewLine
);
5019 else if (m_pOtherView
)
5021 crBkgnd
= m_bDark
? m_ModifiedDarkBk
: m_ModifiedBk
;
5024 if (len
< diff
->modified_length
)
5026 removedPositions
[nTextStartOffset
] = m_bDark
? m_InlineRemovedDarkBk
: m_InlineRemovedBk
;
5028 oLineColors
.SetColor(nTextStartOffset
, crText
, crBkgnd
);
5030 nTextStartOffset
+= static_cast<int>(nTextLength
);
5033 for (std::map
<int, COLORREF
>::const_iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
5035 oLineColors
.AddShotColor(it
->first
, it
->second
);
5037 } while (false); // error catch
5039 if (!m_bWhitespaceInlineDiffs
)
5041 m_ScreenedViewLine
[nViewLine
].lineColors
= oLineColors
;
5042 m_ScreenedViewLine
[nViewLine
].bLineColorsSet
= true;
5046 m_ScreenedViewLine
[nViewLine
].lineColorsWhiteSpace
= oLineColors
;
5047 m_ScreenedViewLine
[nViewLine
].bLineColorsSetWhiteSpace
= true;
5050 return GetLineColors(nViewLine
);
5053 void CBaseView::OnEditSelectall()
5057 int nLastViewLine
= m_pViewData
->GetCount()-1;
5058 if (nLastViewLine
< 0)
5060 SetupAllViewSelection(0, nLastViewLine
);
5062 CString sLine
= GetViewLineChars(nLastViewLine
);
5063 m_ptSelectionViewPosStart
= SetupPoint(0, 0);
5064 m_ptSelectionViewPosEnd
= SetupPoint(sLine
.GetLength(), nLastViewLine
);
5065 m_ptSelectionViewPosOrigin
= SetupPoint(-1, -1);
5070 void CBaseView::FilterWhitespaces(CString
& first
, CString
& second
)
5072 FilterWhitespaces(first
);
5073 FilterWhitespaces(second
);
5076 void CBaseView::FilterWhitespaces(CString
& line
)
5084 int CBaseView::GetButtonEventLineIndex(const POINT
& point
)
5086 if (point
.y
< (GetLineHeight() + HEADERHEIGHT
))
5088 const int nLineFromTop
= (point
.y
- HEADERHEIGHT
) / GetLineHeight();
5089 int nEventLine
= nLineFromTop
+ m_nTopLine
;
5090 nEventLine
--; //we need the index
5095 BOOL
CBaseView::PreTranslateMessage(MSG
* pMsg
)
5097 if (RelayTrippleClick(pMsg
))
5099 return CView::PreTranslateMessage(pMsg
);
5103 void CBaseView::ResetUndoStep()
5108 void CBaseView::SaveUndoStep()
5110 if (!m_AllState
.IsEmpty())
5112 CUndo::GetInstance().AddState(m_AllState
, GetCaretViewPosition());
5117 void CBaseView::SetTheme(bool bDark
)
5119 m_bDark
= bDark
|| CTheme::Instance().IsHighContrastModeDark();
5120 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), m_bDark
);
5122 ModifyStyleEx(WS_EX_CLIENTEDGE
, 0);
5124 ModifyStyleEx(0, WS_EX_CLIENTEDGE
);
5125 CDiffColors::GetInstance().LoadRegistry();
5126 BuildAllScreen2ViewVector();
5127 if (IsWindow(GetSafeHwnd()))
5131 if (FAILED(SetWindowTheme(GetSafeHwnd(), L
"DarkMode_Explorer", nullptr)))
5132 SetWindowTheme(GetSafeHwnd(), L
"Explorer", nullptr);
5135 SetWindowTheme(GetSafeHwnd(), L
"Explorer", nullptr);
5138 m_WhiteSpaceFg
= CRegDWORD(L
"Software\\TortoiseGitMerge\\Colors\\Whitespace", CTheme::Instance().GetThemeColor(GetSysColor(COLOR_3DSHADOW
)));
5141 void CBaseView::InsertViewData(int index
, const CString
& sLine
, DiffState state
, int linenumber
, EOL ending
, HideState hide
, int movedline
)
5143 m_pState
->addedlines
.push_back(index
);
5144 m_pViewData
->InsertData(index
, sLine
, state
, linenumber
, ending
, hide
, movedline
);
5147 void CBaseView::InsertViewData( int index
, const viewdata
& data
)
5149 m_pState
->addedlines
.push_back(index
);
5150 m_pViewData
->InsertData(index
, data
);
5153 void CBaseView::RemoveViewData( int index
)
5155 m_pState
->removedlines
[index
] = m_pViewData
->GetData(index
);
5156 m_pViewData
->RemoveData(index
);
5159 void CBaseView::SetViewData( int index
, const viewdata
& data
)
5161 m_pState
->replacedlines
[index
] = m_pViewData
->GetData(index
);
5162 m_pViewData
->SetData(index
, data
);
5165 void CBaseView::SetViewState(int index
, DiffState state
)
5167 m_pState
->linestates
[index
] = m_pViewData
->GetState(index
);
5168 m_pViewData
->SetState(index
, state
);
5171 void CBaseView::SetViewLine( int index
, const CString
& sLine
)
5173 m_pState
->difflines
[index
] = m_pViewData
->GetLine(index
);
5174 m_pViewData
->SetLine(index
, sLine
);
5177 void CBaseView::SetViewLineNumber( int index
, int linenumber
)
5179 int oldLineNumber
= m_pViewData
->GetLineNumber(index
);
5180 if (oldLineNumber
!= linenumber
) {
5181 m_pState
->linelines
[index
] = oldLineNumber
;
5182 m_pViewData
->SetLineNumber(index
, linenumber
);
5186 void CBaseView::SetViewLineEnding( int index
, EOL ending
)
5188 m_pState
->linesEOL
[index
] = m_pViewData
->GetLineEnding(index
);
5189 m_pViewData
->SetLineEnding(index
, ending
);
5192 void CBaseView::SetViewMarked( int index
, bool marked
)
5194 m_pState
->markedlines
[index
] = m_pViewData
->GetMarked(index
);
5195 m_pViewData
->SetMarked(index
, marked
);
5199 BOOL
CBaseView::GetViewSelection( int& start
, int& end
) const
5203 start
= m_nSelViewBlockStart
;
5204 end
= m_nSelViewBlockEnd
;
5210 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine
)
5212 RebuildIfNecessary();
5213 if ((size() <= screenLine
) || (screenLine
< 0))
5215 return m_Screen2View
[screenLine
].nViewLine
;
5218 int CBaseView::Screen2View::size()
5220 RebuildIfNecessary();
5221 return static_cast<int>(m_Screen2View
.size());
5224 int CBaseView::Screen2View::GetSubLineOffset( int screenLine
)
5226 RebuildIfNecessary();
5227 if (size() <= screenLine
)
5229 return m_Screen2View
[screenLine
].nViewSubLine
;
5233 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
5235 void CBaseView::Screen2View::RebuildIfNecessary()
5238 return; // rebuild not necessary
5240 FixScreenedCacheSize(m_pwndLeft
);
5241 FixScreenedCacheSize(m_pwndRight
);
5242 FixScreenedCacheSize(m_pwndBottom
);
5245 for (auto it
= m_RebuildRanges
.cbegin(); it
!= m_RebuildRanges
.cend(); ++it
)
5247 ResetScreenedViewLineCache(m_pwndLeft
, *it
);
5248 ResetScreenedViewLineCache(m_pwndRight
, *it
);
5249 ResetScreenedViewLineCache(m_pwndBottom
, *it
);
5254 ResetScreenedViewLineCache(m_pwndLeft
);
5255 ResetScreenedViewLineCache(m_pwndRight
);
5256 ResetScreenedViewLineCache(m_pwndBottom
);
5258 m_RebuildRanges
.clear();
5261 size_t OldSize
= m_Screen2View
.size();
5262 m_Screen2View
.clear();
5263 m_Screen2View
.reserve(OldSize
); // guess same size
5264 for (int i
= 0; i
< m_pViewData
->GetCount(); ++i
)
5266 if (m_pMainFrame
->m_bCollapsed
)
5268 while ((i
< m_pViewData
->GetCount())&&(m_pViewData
->GetHideState(i
) == HideState::Hidden
))
5270 if (!(i
< m_pViewData
->GetCount()))
5273 TScreenLineInfo oLineInfo
;
5274 oLineInfo
.nViewLine
= i
;
5275 oLineInfo
.nViewSubLine
= -1; // no wrap
5276 if (m_pMainFrame
->m_bWrapLines
&& !IsViewLineHidden(m_pViewData
, i
))
5279 if (IsLeftViewGood())
5280 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndLeft
->CountMultiLines(i
));
5281 if (IsRightViewGood())
5282 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndRight
->CountMultiLines(i
));
5283 if (IsBottomViewGood())
5284 nMaxLines
= std::max
<int>(nMaxLines
, m_pwndBottom
->CountMultiLines(i
));
5285 for (int l
= 0; l
< (nMaxLines
-1); ++l
)
5287 oLineInfo
.nViewSubLine
++;
5288 m_Screen2View
.push_back(oLineInfo
);
5290 oLineInfo
.nViewSubLine
++;
5292 m_Screen2View
.push_back(oLineInfo
);
5294 m_pViewData
= nullptr;
5296 if (IsLeftViewGood())
5297 m_pwndLeft
->BuildMarkedWordArray();
5298 if (IsRightViewGood())
5299 m_pwndRight
->BuildMarkedWordArray();
5300 if (IsBottomViewGood())
5301 m_pwndBottom
->BuildMarkedWordArray();
5303 RecalcAllVertScrollBars();
5304 RecalcAllHorzScrollBars();
5307 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine
)
5309 RebuildIfNecessary();
5311 int nScreenLineCount
= static_cast<int>(m_Screen2View
.size());
5314 if (nScreenLineCount
>16)
5316 // for enough long data search for last screen
5317 // with viewline less than one we are looking for
5318 // use approximate method (based on) binary search using asymmetric start point
5319 // in form 2**n (determined as MSB of length) to go around division and rounding;
5320 // this effectively looks for bit values from MSB to LSB
5323 //GetMostSignificantBitValue
5324 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
5325 nTestBit
= nScreenLineCount
;
5326 nTestBit
|= nTestBit
>>1;
5327 nTestBit
|= nTestBit
>>2;
5328 nTestBit
|= nTestBit
>>4;
5329 nTestBit
|= nTestBit
>>8;
5330 nTestBit
|= nTestBit
>>16;
5331 nTestBit
^= (nTestBit
>>1);
5335 int nTestPos
= nPos
| nTestBit
;
5336 if (nTestPos
< nScreenLineCount
&& m_Screen2View
[nTestPos
].nViewLine
< viewLine
)
5343 while (nPos
< nScreenLineCount
&& m_Screen2View
[nPos
].nViewLine
< viewLine
)
5351 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData
* pViewData
) {
5354 m_pViewData
= pViewData
;
5357 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData
* pViewData
, int nFirstViewLine
, int nLastViewLine
)
5362 m_pViewData
= pViewData
;
5364 TRebuildRange Range
;
5365 Range
.FirstViewLine
=nFirstViewLine
;
5366 Range
.LastViewLine
=nLastViewLine
;
5367 m_RebuildRanges
.push_back(Range
);
5370 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView
* pwndView
)
5372 if (!IsViewGood(pwndView
))
5376 const int nOldSize
= static_cast<int>(pwndView
->m_ScreenedViewLine
.size());
5377 const int nViewCount
= std::max
<int>(pwndView
->GetViewCount(), 0);
5378 if (nOldSize
== nViewCount
)
5382 pwndView
->m_ScreenedViewLine
.resize(nViewCount
);
5386 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
) const
5388 if (!IsViewGood(pwndView
))
5392 TRebuildRange Range
={0, pwndView
->GetViewCount()-1};
5393 ResetScreenedViewLineCache(pwndView
, Range
);
5397 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView
* pwndView
, const TRebuildRange
& Range
) const
5399 if (!IsViewGood(pwndView
))
5403 if (Range
.LastViewLine
== -1)
5407 ASSERT(Range
.FirstViewLine
>= 0);
5408 ASSERT(Range
.LastViewLine
< pwndView
->GetViewCount());
5409 for (int i
= Range
.FirstViewLine
; i
<= Range
.LastViewLine
; i
++)
5411 pwndView
->m_ScreenedViewLine
[i
].Clear();
5416 void CBaseView::WrapChanged()
5418 m_nMaxLineLength
= -1;
5420 RecalcHorzScrollBar();
5423 void CBaseView::OnEditFind()
5427 m_pFindDialog
->SetFocus();
5432 if (this == m_pwndLeft
)
5434 if (this == m_pwndRight
)
5436 if (this == m_pwndBottom
)
5439 m_pFindDialog
= new CFindDlg(this);
5440 m_pFindDialog
->Create(this, id
);
5442 m_pFindDialog
->SetFindString(HasTextSelection() ? GetSelectedText() : CString());
5443 m_pFindDialog
->SetReadonly(m_bReadonly
);
5446 LRESULT
CBaseView::OnFindDialogMessage(WPARAM wParam
, LPARAM
/*lParam*/)
5448 ASSERT(m_pFindDialog
!= nullptr);
5450 if (m_pFindDialog
->IsTerminating())
5452 // invalidate the handle identifying the dialog box.
5453 m_pFindDialog
= nullptr;
5457 if(m_pFindDialog
->FindNext())
5459 //read data from dialog
5460 m_sFindText
= m_pFindDialog
->GetFindString();
5461 m_bMatchCase
= (m_pFindDialog
->MatchCase() == TRUE
);
5462 m_bLimitToDiff
= m_pFindDialog
->LimitToDiffs();
5463 m_bWholeWord
= m_pFindDialog
->WholeWord();
5466 m_sFindText
= m_sFindText
.MakeLower();
5468 BuildFindStringArray();
5469 if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Find
)
5471 if (m_pFindDialog
->SearchUp())
5476 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Count
)
5479 for (size_t i
= 0; i
< m_arFindStringLines
.size(); ++i
)
5480 count
+= m_arFindStringLines
[i
];
5482 matches
.Format(IDS_FIND_COUNT
, count
);
5483 m_pFindDialog
->SetStatusText(matches
);
5485 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::Replace
)
5489 bool bFound
= false;
5490 if (m_pFindDialog
->SearchUp())
5491 bFound
= Search(SearchPrevious
, true, true, false);
5493 bFound
= Search(SearchNext
, true, true, false);
5496 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5497 CUndo::GetInstance().BeginGrouping();
5498 RemoveSelectedText();
5499 InsertText(sReplaceText
);
5500 CUndo::GetInstance().EndGrouping();
5504 else if (static_cast<CFindDlg::FindType
>(wParam
) == CFindDlg::FindType::ReplaceAll
)
5508 bool bFound
= false;
5509 int replaceCount
= 0;
5510 POINT lastPoint
= m_ptSelectionViewPosStart
;
5511 m_ptSelectionViewPosStart
.x
= m_ptSelectionViewPosStart
.y
= 0;
5512 CUndo::GetInstance().BeginGrouping();
5515 bFound
= Search(SearchNext
, true, false, true);
5518 CString sReplaceText
= m_pFindDialog
->GetReplaceString();
5519 RemoveSelectedText();
5520 InsertText(sReplaceText
);
5524 CUndo::GetInstance().EndGrouping();
5525 if (replaceCount
== 0)
5526 m_ptSelectionViewPosStart
= lastPoint
;
5528 message
.Format(IDS_FIND_REPLACED
, replaceCount
);
5529 m_pFindDialog
->SetStatusText(message
, RGB(0, 0, 0));
5536 void CBaseView::OnEditFindnextStart()
5540 if (HasTextSelection())
5542 m_sFindText
= GetSelectedText();
5543 m_bMatchCase
= false;
5544 m_bLimitToDiff
= false;
5545 m_bWholeWord
= false;
5546 m_sFindText
= m_sFindText
.MakeLower();
5548 BuildFindStringArray();
5553 m_sFindText
.Empty();
5554 BuildFindStringArray();
5558 void CBaseView::OnEditFindprevStart()
5562 if (HasTextSelection())
5564 m_sFindText
= GetSelectedText();
5565 m_bMatchCase
= false;
5566 m_bLimitToDiff
= false;
5567 m_bWholeWord
= false;
5568 m_sFindText
= m_sFindText
.MakeLower();
5570 BuildFindStringArray();
5575 m_sFindText
.Empty();
5576 BuildFindStringArray();
5580 bool CBaseView::StringFound(const CString
& str
, SearchDirection srchDir
, int& start
, int& end
) const
5585 if (srchDir
== SearchPrevious
)
5588 int laststart2
= -1;
5591 laststart2
= laststart
;
5592 laststart
= str
.Find(m_sFindText
, laststart
+ 1);
5593 } while (laststart
>= 0 && laststart
< start
);
5597 start
= str
.Find(m_sFindText
, start
);
5598 end
= start
+ m_sFindText
.GetLength();
5599 bStringFound
= (start
>= 0);
5600 if (bStringFound
&& m_bWholeWord
)
5603 bStringFound
= IsWordSeparator(str
.Mid(start
- 1, 1).GetAt(0));
5607 if (str
.GetLength() > end
)
5608 bStringFound
= IsWordSeparator(str
.Mid(end
, 1).GetAt(0));
5613 if (srchDir
== SearchPrevious
)
5619 } while (!bStringFound
&& start
>= 0);
5621 return bStringFound
;
5624 void CBaseView::OnEditFindprev()
5626 Search(SearchPrevious
, false, true, false);
5629 void CBaseView::OnEditFindnext()
5631 Search(SearchNext
, false, true, false);
5634 bool CBaseView::Search(SearchDirection srchDir
, bool useStart
, bool flashIfNotFound
, bool stopEof
)
5636 if (m_sFindText
.IsEmpty())
5641 POINT start
= useStart
? m_ptSelectionViewPosStart
: m_ptSelectionViewPosEnd
;
5643 end
.y
= m_pViewData
->GetCount()-1;
5647 if (srchDir
==SearchNext
)
5648 end
.x
= GetViewLineLength(end
.y
);
5651 end
.x
= m_ptSelectionViewPosStart
.x
;
5655 if (!HasTextSelection())
5657 start
.y
= m_ptCaretViewPos
.y
;
5658 if (srchDir
==SearchNext
)
5659 start
.x
= m_ptCaretViewPos
.x
;
5663 end
.x
= m_ptCaretViewPos
.x
;
5666 CString sSelectedText
;
5668 for (int nViewLine
=start
.y
; ;srchDir
==SearchNext
? nViewLine
++ : nViewLine
--)
5674 nViewLine
= m_pViewData
->GetCount()-1;
5675 startline
= start
.y
;
5676 if (flashIfNotFound
)
5679 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_TOPREACHED
)), RGB(63, 127, 47));
5680 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5683 if (nViewLine
> end
.y
)
5688 startline
= start
.y
;
5689 if (flashIfNotFound
)
5692 m_pFindDialog
->SetStatusText(CString(MAKEINTRESOURCE(IDS_FIND_BOTTOMREACHED
)), RGB(63, 127, 47));
5693 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 2, 100);
5696 switch (m_pViewData
->GetState(nViewLine
))
5698 case DiffState::Empty
:
5700 case DiffState::Unknown
:
5701 case DiffState::Normal
:
5702 case DiffState::FilteredDiff
:
5706 case DiffState::Removed
:
5707 case DiffState::RemovedWhitespace
:
5708 case DiffState::Added
:
5709 case DiffState::AddedWhitespace
:
5710 case DiffState::Whitespace
:
5711 case DiffState::WhitespaceDiff
:
5712 case DiffState::Conflicted
:
5713 case DiffState::Conflicted_Ignored
:
5714 case DiffState::ConflictAdded
:
5715 case DiffState::ConflictEmpty
:
5716 case DiffState::ConflictResolved
:
5717 case DiffState::IdenticalRemoved
:
5718 case DiffState::IdenticalAdded
:
5719 case DiffState::TheirsRemoved
:
5720 case DiffState::TheirsAdded
:
5721 case DiffState::YoursRemoved
:
5722 case DiffState::YoursAdded
:
5723 case DiffState::Edited
:
5725 sSelectedText
= GetViewLineChars(nViewLine
);
5726 if (nViewLine
== start
.y
&& startline
< 0)
5727 sSelectedText
= srchDir
== SearchNext
? sSelectedText
.Mid(start
.x
) : sSelectedText
.Left(end
.x
);
5729 sSelectedText
= sSelectedText
.MakeLower();
5730 int startfound
= srchDir
== SearchNext
? 0 : sSelectedText
.GetLength();
5732 if (StringFound(sSelectedText
, srchDir
, startfound
, endfound
))
5734 HighlightViewLines(nViewLine
, nViewLine
);
5735 m_ptSelectionViewPosStart
.x
= startfound
;
5736 m_ptSelectionViewPosEnd
.x
= endfound
;
5737 if (nViewLine
== start
.y
&& startline
< 0)
5739 m_ptSelectionViewPosStart
.x
+= start
.x
;
5740 m_ptSelectionViewPosEnd
.x
+= start
.x
;
5742 m_ptSelectionViewPosEnd
.x
= m_ptSelectionViewPosStart
.x
+ m_sFindText
.GetLength();
5743 m_ptSelectionViewPosStart
.y
= nViewLine
;
5744 m_ptSelectionViewPosEnd
.y
= nViewLine
;
5745 m_ptCaretViewPos
= m_ptSelectionViewPosStart
;
5746 UpdateViewsCaretPosition();
5747 EnsureCaretVisible();
5757 if (nViewLine
== startline
)
5759 if (flashIfNotFound
)
5762 message
.Format(IDS_FIND_NOTFOUND
, static_cast<LPCWSTR
>(m_sFindText
));
5764 m_pFindDialog
->SetStatusText(message
, RGB(255, 0, 0));
5765 ::MessageBeep(0xFFFFFFFF);
5766 m_pMainFrame
->FlashWindowEx(FLASHW_ALL
, 3, 100);
5772 m_pMainFrame
->m_nMoveMovesToIgnore
= MOVESTOIGNORE
;
5776 CString
CBaseView::GetSelectedText() const
5778 CString sSelectedText
;
5779 POINT start
= m_ptSelectionViewPosStart
;
5780 POINT end
= m_ptSelectionViewPosEnd
;
5781 if (!HasTextSelection())
5783 if (!HasSelection())
5784 return sSelectedText
;
5785 start
.y
= m_nSelViewBlockStart
;
5787 end
.y
= m_nSelViewBlockEnd
;
5788 end
.x
= GetViewLineLength(m_nSelViewBlockEnd
);
5791 return sSelectedText
;
5792 // first store the selected lines in one CString
5793 for (int nViewLine
=start
.y
; nViewLine
<=end
.y
; nViewLine
++)
5795 switch (m_pViewData
->GetState(nViewLine
))
5797 case DiffState::Empty
:
5799 case DiffState::Unknown
:
5800 case DiffState::Normal
:
5801 case DiffState::Removed
:
5802 case DiffState::RemovedWhitespace
:
5803 case DiffState::Added
:
5804 case DiffState::AddedWhitespace
:
5805 case DiffState::Whitespace
:
5806 case DiffState::WhitespaceDiff
:
5807 case DiffState::Conflicted
:
5808 case DiffState::Conflicted_Ignored
:
5809 case DiffState::ConflictAdded
:
5810 case DiffState::ConflictEmpty
:
5811 case DiffState::ConflictResolved
:
5812 case DiffState::IdenticalRemoved
:
5813 case DiffState::IdenticalAdded
:
5814 case DiffState::TheirsRemoved
:
5815 case DiffState::TheirsAdded
:
5816 case DiffState::YoursRemoved
:
5817 case DiffState::YoursAdded
:
5818 case DiffState::Edited
:
5819 case DiffState::FilteredDiff
:
5820 sSelectedText
+= GetViewLineChars(nViewLine
);
5821 sSelectedText
+= L
"\r\n";
5825 // remove the non-selected chars from the first line, last line and last \r\n
5826 int nLeftCut
= start
.x
;
5827 int nRightCut
= GetViewLineChars(end
.y
).GetLength() - end
.x
+ 2;
5828 sSelectedText
= sSelectedText
.Mid(nLeftCut
, sSelectedText
.GetLength()-nLeftCut
-nRightCut
);
5829 return sSelectedText
;
5832 void CBaseView::CheckModifications(bool& hasMods
, bool& hasConflicts
, bool& hasWhitespaceMods
, bool& hasFilteredMods
)
5835 hasConflicts
= false;
5836 hasWhitespaceMods
= false;
5837 hasFilteredMods
= false;
5841 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5843 DiffState state
= m_pViewData
->GetState(i
);
5846 case DiffState::Added
:
5847 case DiffState::IdenticalAdded
:
5848 case DiffState::TheirsAdded
:
5849 case DiffState::YoursAdded
:
5850 case DiffState::ConflictAdded
:
5851 case DiffState::IdenticalRemoved
:
5852 case DiffState::Removed
:
5853 case DiffState::TheirsRemoved
:
5854 case DiffState::YoursRemoved
:
5855 case DiffState::Empty
:
5858 case DiffState::Conflicted
:
5859 case DiffState::Conflicted_Ignored
:
5860 hasConflicts
= true;
5862 case DiffState::RemovedWhitespace
:
5863 case DiffState::AddedWhitespace
:
5864 case DiffState::Whitespace
:
5865 case DiffState::WhitespaceDiff
:
5866 hasWhitespaceMods
= true;
5868 case DiffState::FilteredDiff
:
5869 hasFilteredMods
= true;
5876 void CBaseView::OnEditGotoline()
5880 // find the last and first line number
5881 int nViewLineCount
= m_pViewData
->GetCount();
5883 int nLastLineNumber
= DIFF_EMPTYLINENUMBER
;
5884 for (int nViewLine
=nViewLineCount
-1; nViewLine
>=0; --nViewLine
)
5886 nLastLineNumber
= m_pViewData
->GetLineNumber(nViewLine
);
5887 if (nLastLineNumber
!=DIFF_EMPTYLINENUMBER
)
5892 if (nLastLineNumber
==DIFF_EMPTYLINENUMBER
|| nLastLineNumber
==0) // not numbered line foud or last one is first
5897 int nFirstLineNumber
=1; // first is always 1
5900 sText
.FormatMessage(IDS_GOTOLINE
, nFirstLineNumber
, nLastLineNumber
);
5902 CGotoLineDlg
dlg(this);
5903 dlg
.SetLabel(sText
);
5904 dlg
.SetLimits(nFirstLineNumber
, nLastLineNumber
);
5905 if (dlg
.DoModal() == IDOK
)
5907 for (int nViewLine
= 0; nViewLine
< nViewLineCount
; ++nViewLine
)
5909 if ((m_pViewData
->GetLineNumber(nViewLine
)+1) == dlg
.GetLineNumber())
5911 HighlightViewLines(nViewLine
, nViewLine
);
5918 int CBaseView::SaveFile(int nFlags
)
5921 if (m_pViewData
&& m_pWorkingFile
)
5923 CFileTextLines file
;
5924 m_SaveParams
.m_LineEndings
= m_lineendings
;
5925 m_SaveParams
.m_UnicodeType
= m_texttype
;
5926 file
.SetSaveParams(m_SaveParams
);
5928 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
5930 //only copy non-removed lines
5931 DiffState state
= m_pViewData
->GetState(i
);
5934 case DiffState::Conflicted
:
5935 case DiffState::Conflicted_Ignored
:
5942 } while((last
<m_pViewData
->GetCount()) && ((m_pViewData
->GetState(last
)==DiffState::Conflicted
)||(m_pViewData
->GetState(last
)==DiffState::Conflicted_Ignored
)));
5943 file
.Add(L
"<<<<<<< .mine", EOL::NoEnding
);
5944 for (int j
=first
; j
<last
; j
++)
5946 file
.Add(m_pwndRight
->m_pViewData
->GetLine(j
), m_pwndRight
->m_pViewData
->GetLineEnding(j
));
5948 file
.Add(L
"=======", EOL::NoEnding
);
5949 for (int j
=first
; j
<last
; j
++)
5951 file
.Add(m_pwndLeft
->m_pViewData
->GetLine(j
), m_pwndLeft
->m_pViewData
->GetLineEnding(j
));
5953 file
.Add(L
">>>>>>> .theirs", EOL::NoEnding
);
5957 case DiffState::Empty
:
5959 case DiffState::ConflictEmpty
:
5960 case DiffState::IdenticalRemoved
:
5961 case DiffState::Removed
:
5962 case DiffState::TheirsRemoved
:
5963 case DiffState::YoursRemoved
:
5964 case DiffState::ConflictResolvedEmpty
:
5965 if ((nFlags
&SAVE_REMOVEDLINES
) == 0)
5967 // do not save removed lines
5971 file
.Add(m_pViewData
->GetLine(i
), m_pViewData
->GetLineEnding(i
));
5975 CString filename
= m_pWorkingFile
->GetFilename();
5976 if (m_pWorkingFile
->IsReadonly())
5977 if (!CCommonAppUtils::FileOpenSave(filename
, nullptr, IDS_SAVEASTITLE
, IDS_COMMONFILEFILTER
, false, m_hWnd
))
5979 if (!file
.Save(filename
))
5981 ::MessageBox(m_hWnd
, file
.GetErrorString(), L
"TortoiseGitMerge", MB_ICONERROR
);
5984 m_pWorkingFile
->SetFileName(filename
);
5985 m_pWorkingFile
->StoreFileAttributes();
5986 // m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
5988 CUndo::GetInstance().MarkAsOriginalState(
5990 this == m_pwndRight
,
5991 this == m_pwndBottom
);
5992 if (file
.GetCount() == 1 && file
.GetAt(0).IsEmpty() && file
.GetLineEnding(0) == EOL::NoEnding
)
5994 return file
.GetCount();
6000 int CBaseView::SaveFileTo(CString sFileName
, int nFlags
)
6004 m_pWorkingFile
->SetFileName(sFileName
);
6005 return SaveFile(nFlags
);
6011 EOL
CBaseView::GetLineEndings()
6013 return GetLineEndings(GetWhitecharsProperties().HasMixedEols
);
6016 EOL
CBaseView::GetLineEndings(bool bHasMixedEols
)
6020 return EOL::AutoLine
; // mixed eols - hack value
6022 if (m_lineendings
== EOL::AutoLine
)
6026 return m_lineendings
;
6029 void CBaseView::ReplaceLineEndings(EOL eEol
)
6031 if (eEol
== EOL::AutoLine
)
6036 m_lineendings
= eEol
;
6037 // replace all set EOLs
6038 // TODO store line endings and lineendings in undo
6039 //CUndo::BeginGrouping();
6040 for (int i
= 0; i
< GetViewCount(); ++i
)
6046 EOL eLineEol
= GetViewLineEnding(i
);
6047 if (eLineEol
== EOL::AutoLine
|| eLineEol
== EOL::NoEnding
|| eLineEol
== m_lineendings
)
6051 SetViewLineEnding(i
, eEol
);
6053 //CUndo::EndGrouping();
6054 //CUndo::saveundostep;
6059 void CBaseView::SetLineEndingStyle(EOL eEol
)
6061 m_lineendings
= eEol
;
6064 void CBaseView::SetTextType(CFileTextLines::UnicodeType eTextType
)
6066 if (m_texttype
== eTextType
)
6070 m_texttype
= eTextType
;
6075 void CBaseView::AskUserForNewLineEndingsAndTextType(int nTextId
)
6078 return; // nothing to be changed in read-only view
6080 dlg
.view
.LoadString(nTextId
);
6081 dlg
.texttype
= m_texttype
;
6082 dlg
.lineendings
= GetLineEndings();
6083 if (dlg
.DoModal() != IDOK
)
6085 SetTextType(dlg
.texttype
);
6086 ReplaceLineEndings(dlg
.lineendings
);
6090 Replaces lines from source view to this
6092 void CBaseView::UseViewBlock(CBaseView
* pwndView
, int nFirstViewLine
, int nLastViewLine
, std::function
<bool(int)> fnSkip
)
6094 if (!IsViewGood(pwndView
))
6098 CUndo::GetInstance().BeginGrouping();
6100 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6102 bool skip
= fnSkip(viewLine
);
6105 if (GetViewMarked(viewLine
))
6106 SetViewMarked(viewLine
, false);
6109 viewdata line
= pwndView
->GetViewData(viewLine
);
6110 if (line
.ending
!= EOL::NoEnding
)
6111 line
.ending
= m_lineendings
;
6114 case DiffState::ConflictEmpty
:
6115 case DiffState::Unknown
:
6116 line
.state
= DiffState::Empty
;
6118 case DiffState::Empty
:
6120 case DiffState::Added
:
6121 case DiffState::ConflictAdded
:
6122 case DiffState::Conflicted
:
6123 case DiffState::Conflicted_Ignored
:
6124 case DiffState::IdenticalAdded
:
6125 case DiffState::TheirsAdded
:
6126 case DiffState::YoursAdded
:
6127 case DiffState::IdenticalRemoved
:
6128 case DiffState::Removed
:
6129 case DiffState::TheirsRemoved
:
6130 case DiffState::YoursRemoved
:
6131 pwndView
->SetViewState(viewLine
, DiffState::Normal
);
6132 line
.state
= DiffState::Normal
;
6134 case DiffState::Normal
:
6139 bool marked
= GetViewMarked(viewLine
);
6140 SetViewData(viewLine
, line
);
6142 SetViewMarked(viewLine
, false);
6143 if ((m_texttype
== UnicodeType::ASCII
) && (pwndView
->GetTextType() != UnicodeType::ASCII
))
6145 // if this view is in ASCII and the other is not, we have to make sure that
6146 // the text we copy from the other view can actually be saved in ASCII encoding.
6147 // if not, we have to change this views encoding to the same encoding as the other view
6148 BOOL useDefault
= FALSE
;
6149 WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, line
.sLine
, -1, nullptr, 0, 0, &useDefault
);
6150 if (useDefault
) // a default char is required, so the char can not be saved as ASCII
6151 SetTextType(pwndView
->GetTextType());
6154 // normal lines is mostly same but may differ in EOL so any copied line change view state to modified
6155 // TODO: check if copied line is same as original one set modified only when differ
6159 int nRemovedLines
= CleanEmptyLines();
6162 // make sure all non empty line have EOL set but last
6163 // wrong can be last copied line(have eol, but no line under),
6164 // or old last line (line before copied block missing eol, but have line under)
6165 // we'll check all lines to be sure
6166 int nLine
= GetViewCount();
6167 // check last line have no EOL set
6170 if (!IsViewLineEmpty(nLine
))
6172 if (GetViewLineEnding(nLine
) != EOL::NoEnding
)
6174 // we added non last line into empty block on the end (or should we remove eol from this one ?)
6175 // so next line is empty
6176 ASSERT(IsViewLineEmpty(nLine
+1));
6177 // and we can turn it to normal empty line
6178 SetViewData(nLine
+ 1, viewdata(CString(), DiffState::Added
, 1, EOL::NoEnding
, HideState::Shown
));
6183 // check all (nonlast) line have EOL set
6186 if (!IsViewLineEmpty(nLine
))
6188 if (GetViewLineEnding(nLine
) == EOL::NoEnding
)
6190 SetViewLineEnding(nLine
, m_lineendings
);
6191 // in theory there should be only one line needing fix, but most of time we get over all anyway
6197 UpdateViewLineNumbers();
6200 CUndo::GetInstance().EndGrouping();
6202 if (nRemovedLines
!=0)
6204 // some lines are gone update selection
6206 SetupAllViewSelection(nFirstViewLine
, nLastViewLine
- nRemovedLines
);
6208 BuildAllScreen2ViewVector();
6209 pwndView
->Invalidate();
6213 void CBaseView::MarkBlock(bool marked
, int nFirstViewLine
, int nLastViewLine
)
6217 CUndo::GetInstance().BeginGrouping();
6219 for (int viewLine
= nFirstViewLine
; viewLine
<= nLastViewLine
; viewLine
++)
6220 SetViewMarked(viewLine
, marked
);
6224 CUndo::GetInstance().EndGrouping();
6226 BuildAllScreen2ViewVector();
6231 void CBaseView::LeaveOnlyMarkedBlocks(CBaseView
*pwndView
)
6233 auto fn
= [this](int viewLine
) -> bool { return GetViewMarked(viewLine
) || GetViewState(viewLine
) == DiffState::Edited
; };
6234 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6237 void CBaseView::UseViewFileOfMarked(CBaseView
*pwndView
)
6239 auto fn
= [this](int viewLine
) -> bool { return !GetViewMarked(viewLine
) || GetViewState(viewLine
) == DiffState::Edited
; };
6240 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6243 void CBaseView::UseViewFileExceptEdited(CBaseView
*pwndView
)
6245 auto fn
= [this](int viewLine
) -> bool { return GetViewState(viewLine
) == DiffState::Edited
; };
6246 UseViewBlock(pwndView
, 0, GetViewCount() - 1, fn
);
6249 int CBaseView::GetLargestSpaceStreak(const CString
& line
)
6253 for (int i
= 0; i
< line
.GetLength(); ++i
)
6259 maxstreak
= std::max(count
, maxstreak
);
6263 return std::max(count
, maxstreak
);
6266 int CBaseView::GetIndentCharsForLine(int x
, int y
)
6268 const int maxGuessLine
= 100;
6270 const CString
& line
= GetViewLine(y
);
6271 if (m_nTabMode
& TABMODE_SMARTINDENT
)
6273 // if the line contains one tab, use tabs
6274 // we can not test for spaces, since even if tabs are used,
6275 // spaces are used in a tabified file for alignment.
6276 if (line
.Find(L
'\t') >= 0)
6277 nTabMode
= 0; // use tabs
6278 else if (GetLargestSpaceStreak(line
) > m_nTabSize
)
6279 nTabMode
= 1; // use spaces
6281 // detect lines nearby
6282 for (int i
= y
- 1, j
= y
+ 1; nTabMode
== -1; --i
, ++j
)
6284 bool above
= i
>= 0 && i
>= y
- maxGuessLine
;
6285 bool below
= j
< GetViewCount() && j
<= y
+ maxGuessLine
;
6286 if (!(above
|| below
))
6288 auto ac
= CString();
6289 auto bc
= CString();
6291 ac
= GetViewLine(i
);
6293 bc
= GetViewLine(j
);
6294 if ((ac
.Find(L
'\t') >= 0) || (bc
.Find(L
'\t') >= 0))
6299 else if ((GetLargestSpaceStreak(ac
) > m_nTabSize
) && (GetLargestSpaceStreak(bc
) > m_nTabSize
))
6307 nTabMode
= m_nTabMode
& TABMODE_USESPACES
;
6312 x
= CountExpandedChars(line
, x
);
6313 return (m_nTabSize
- (x
% m_nTabSize
));
6320 void CBaseView::AddIndentationForSelectedBlock()
6322 bool bModified
= false;
6323 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6325 // skip the line if no character is selected in the last selected line
6326 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6331 if (IsLineEmpty(nViewLine
))
6335 const CString
&sLine
= GetViewLine(nViewLine
);
6336 CString sTemp
= sLine
;
6337 if (sTemp
.Trim().IsEmpty())
6339 // skip empty and whitechar only lines
6342 // add tab to line start (alternatively m_nTabSize spaces can be used)
6344 int indentChars
= GetIndentCharsForLine(0, nViewLine
);
6345 tabStr
= indentChars
> 0 ? CString(L
' ', indentChars
) : CString("\t");
6346 SetViewLine(nViewLine
, tabStr
+ sLine
);
6353 BuildAllScreen2ViewVector();
6357 void CBaseView::RemoveIndentationForSelectedBlock()
6359 bool bModified
= false;
6360 for (int nViewLine
= m_ptSelectionViewPosStart
.y
; nViewLine
<= m_ptSelectionViewPosEnd
.y
; nViewLine
++)
6362 // skip the line if no character is selected in the last selected line
6363 if (nViewLine
== m_ptSelectionViewPosEnd
.y
&& m_ptSelectionViewPosEnd
.x
== 0)
6368 if (IsLineEmpty(nViewLine
))
6372 CString sLine
= GetViewLine(nViewLine
);
6373 // remove up to n spaces from line start
6374 // and one tab (if less then n spaces was removed)
6376 while (nPos
<m_nTabSize
)
6378 switch (sLine
[nPos
])
6390 sLine
.Delete(0, nPos
);
6391 SetViewLine(nViewLine
, sLine
);
6399 BuildAllScreen2ViewVector();
6404 there are two possible versions
6405 - convert tabs to spaces only in front of text (implemented)
6406 - convert all tabs to spaces
6408 void CBaseView::ConvertTabToSpaces()
6410 bool bModified
= false;
6411 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6413 if (IsLineEmpty(nViewLine
))
6417 const CString
&sLine
= GetViewLine(nViewLine
);
6418 bool bTabToConvertFound
= false;
6421 while (nPosIn
<sLine
.GetLength())
6423 switch (sLine
[nPosIn
])
6431 bTabToConvertFound
= true;
6432 nPosOut
= (nPosOut
+m_nTabSize
) - nPosOut
%m_nTabSize
;
6437 if (bTabToConvertFound
)
6439 CString sLineNew
= sLine
;
6440 sLineNew
.Delete(0, nPosIn
);
6441 sLineNew
= CString(' ', nPosOut
) + sLineNew
;
6442 SetViewLine(nViewLine
, sLineNew
);
6450 BuildAllScreen2ViewVector();
6455 there are two possible version
6456 - convert spaces to tabs only in front of text (implemented)
6457 - convert all spaces to tabs
6459 void CBaseView::Tabularize()
6461 bool bModified
= false;
6462 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6464 if (IsLineEmpty(nViewLine
))
6468 const CString
&sLine
= GetViewLine(nViewLine
);
6470 int nTabCount
= 0; // total tabs to be used
6471 int nSpaceCount
= 0; // number of spaces in tab size run
6473 while (nPos
<sLine
.GetLength())
6475 switch (sLine
[nPos
++])
6479 if (++nSpaceCount
< m_nTabSize
)
6494 CString sLineNew
= sLine
;
6495 sLineNew
.Delete(0, nDel
);
6496 sLineNew
= CString('\t', nTabCount
) + sLineNew
;
6497 if (sLine
!=sLineNew
)
6499 SetViewLine(nViewLine
, sLineNew
);
6508 BuildAllScreen2ViewVector();
6512 void CBaseView::RemoveTrailWhiteChars()
6514 bool bModified
= false;
6515 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6517 if (IsLineEmpty(nViewLine
))
6521 const CString
&sLine
= GetViewLine(nViewLine
);
6522 CString sLineNew
= sLine
;
6523 sLineNew
.TrimRight();
6524 if (sLine
.GetLength()!=sLineNew
.GetLength())
6526 SetViewLine(nViewLine
, sLineNew
);
6534 BuildAllScreen2ViewVector();
6538 CBaseView::TWhitecharsProperties
CBaseView::GetWhitecharsProperties()
6540 if (GetViewCount()>10000)
6542 // 10k lines is enough to check
6543 TWhitecharsProperties oRet
= {true, true, true, true};
6546 TWhitecharsProperties oRet
= {};
6547 for (int nViewLine
= 0; nViewLine
< GetViewCount(); nViewLine
++)
6549 if (IsLineEmpty(nViewLine
))
6553 const CString
&sLine
= GetViewLine(nViewLine
);
6554 if (sLine
.IsEmpty())
6558 // check leading whites for convertible tabs and spaces
6560 int nSpaceCount
= 0; // number of spaces in tab size run
6561 while (nPos
<sLine
.GetLength() && (!oRet
.HasSpacesToConvert
|| !oRet
.HasTabsToConvert
))
6563 switch (sLine
[nPos
++])
6566 if (++nSpaceCount
>= m_nTabSize
)
6568 oRet
.HasSpacesToConvert
= true;
6572 oRet
.HasTabsToConvert
= true;
6575 oRet
.HasSpacesToConvert
= true;
6582 // check trailing whites for removable chars
6583 switch (sLine
[sLine
.GetLength()-1])
6587 oRet
.HasTrailWhiteChars
= true;
6591 EOL eLineEol
= GetViewLineEnding(nViewLine
);
6592 if (!oRet
.HasMixedEols
&& (eLineEol
!= m_lineendings
) && (eLineEol
!= EOL::AutoLine
) && (eLineEol
!= EOL::NoEnding
))
6594 oRet
.HasMixedEols
= true;
6600 void CBaseView::InsertText(const CString
& sText
)
6604 POINT ptCaretViewPos
= GetCaretViewPosition();
6605 int nLeft
= ptCaretViewPos
.x
;
6606 int nViewLine
= ptCaretViewPos
.y
;
6608 if ((nViewLine
== 0) && (GetViewCount() == 0))
6609 OnChar(VK_RETURN
, 0, 0);
6611 std::vector
<CString
> lines
;
6614 while ((nEolPos
= sText
.Find('\r', nEolPos
)) >= 0)
6616 CString sLine
= sText
.Mid(nStart
, nEolPos
- nStart
);
6617 lines
.push_back(sLine
);
6621 CString sLine
= sText
.Mid(nStart
);
6622 lines
.push_back(sLine
);
6624 int nLinesToPaste
= static_cast<int>(lines
.size());
6625 if (nLinesToPaste
> 1)
6629 // We want to undo the multiline insertion in a single step.
6630 CUndo::GetInstance().BeginGrouping();
6632 sLine
= GetViewLineChars(nViewLine
);
6633 CString sLineLeft
= sLine
.Left(nLeft
);
6634 CString sLineRight
= sLine
.Right(sLine
.GetLength() - nLeft
);
6635 EOL eOriginalEnding
= GetViewLineEnding(nViewLine
);
6636 viewdata
newLine(L
"", DiffState::Edited
, 1, m_lineendings
, HideState::Shown
);
6637 if (!lines
[0].IsEmpty() || !sLineRight
.IsEmpty() || (eOriginalEnding
!= m_lineendings
))
6639 newLine
.sLine
= sLineLeft
+ lines
[0];
6640 SetViewData(nViewLine
, newLine
);
6643 int nInsertLine
= nViewLine
;
6644 for (int i
= 1; i
< nLinesToPaste
- 1; i
++)
6646 newLine
.sLine
= lines
[i
];
6647 InsertViewData(++nInsertLine
, newLine
);
6649 newLine
.sLine
= lines
[nLinesToPaste
- 1] + sLineRight
;
6650 newLine
.ending
= eOriginalEnding
;
6651 InsertViewData(++nInsertLine
, newLine
);
6656 // adds new lines everywhere except me
6657 if (IsViewGood(m_pwndLeft
) && m_pwndLeft
!= this)
6659 m_pwndLeft
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6661 if (IsViewGood(m_pwndRight
) && m_pwndRight
!= this)
6663 m_pwndRight
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6665 if (IsViewGood(m_pwndBottom
) && m_pwndBottom
!= this)
6667 m_pwndBottom
->InsertViewEmptyLines(nViewLine
+ 1, nLinesToPaste
- 1);
6669 UpdateViewLineNumbers();
6672 CUndo::GetInstance().EndGrouping();
6674 ptCaretViewPos
= SetupPoint(lines
[nLinesToPaste
- 1].GetLength(), nInsertLine
);
6678 // single line text - just insert it
6679 sLine
= GetViewLineChars(nViewLine
);
6680 sLine
.Insert(nLeft
, sText
);
6681 ptCaretViewPos
= SetupPoint(nLeft
+ sText
.GetLength(), nViewLine
);
6682 SetViewLine(nViewLine
, sLine
);
6684 auto viewState
= GetViewState(nViewLine
);
6685 if (IsStateEmpty(viewState
) || IsStateConflicted(viewState
) || viewState
== DiffState::IdenticalRemoved
)
6687 // if not last line set EOL
6688 for (int nCheckViewLine
= nViewLine
+ 1; nCheckViewLine
< GetViewCount(); ++nCheckViewLine
)
6690 if (!IsViewLineEmpty(nCheckViewLine
))
6692 SetViewLineEnding(nViewLine
, m_lineendings
);
6696 // make sure previous (non empty) line have EOL set
6697 for (int nCheckViewLine
= nViewLine
- 1; nCheckViewLine
> 0; --nCheckViewLine
)
6699 if (!IsViewLineEmpty(nCheckViewLine
))
6701 if (GetViewLineEnding(nCheckViewLine
) == EOL::NoEnding
)
6702 SetViewLineEnding(nCheckViewLine
, m_lineendings
);
6708 SetViewState(nViewLine
, DiffState::Edited
);
6714 BuildAllScreen2ViewVector();
6715 UpdateCaretViewPosition(ptCaretViewPos
);
6718 ULONG
CBaseView::GetGestureStatus(CPoint
/*ptTouch*/)