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