1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2003-2009,2011 - TortoiseSVN
4 // Copyright (C) 2011 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"
35 #define MARGINWIDTH 20
36 #define HEADERHEIGHT 10
40 #define INLINEADDED_COLOR RGB(255, 255, 150)
41 #define INLINEREMOVED_COLOR RGB(200, 100, 100)
42 #define MODIFIED_COLOR RGB(220, 220, 255)
44 #define IDT_SCROLLTIMER 101
46 CBaseView
* CBaseView::m_pwndLeft
= NULL
;
47 CBaseView
* CBaseView::m_pwndRight
= NULL
;
48 CBaseView
* CBaseView::m_pwndBottom
= NULL
;
49 CLocatorBar
* CBaseView::m_pwndLocator
= NULL
;
50 CLineDiffBar
* CBaseView::m_pwndLineDiffBar
= NULL
;
51 CMFCStatusBar
* CBaseView::m_pwndStatusBar
= NULL
;
52 CMainFrame
* CBaseView::m_pMainFrame
= NULL
;
54 IMPLEMENT_DYNCREATE(CBaseView
, CView
)
56 CBaseView::CBaseView()
58 m_pCacheBitmap
= NULL
;
60 m_pOtherViewData
= NULL
;
64 m_nMaxLineLength
= -1;
70 m_bMouseWithin
= FALSE
;
72 lineendings
= EOL_AUTOLINE
;
73 m_bCaretHidden
= true;
77 m_ptSelectionStartPos
= m_ptCaretPos
;
78 m_ptSelectionEndPos
= m_ptCaretPos
;
79 m_ptSelectionOrigin
= m_ptCaretPos
;
81 m_bShowSelection
= true;
82 texttype
= CFileTextLines::AUTOTYPE
;
83 m_bViewWhitespace
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewWhitespaces"), 1);
84 m_bViewLinenumbers
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
85 m_bShowInlineDiff
= CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE
);
86 m_InlineAddedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR
);
87 m_InlineRemovedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR
);
88 m_ModifiedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR
);
89 m_WhiteSpaceFg
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT
));
90 m_sWordSeparators
= CRegString(_T("Software\\TortoiseMerge\\WordSeparators"), _T("[]();.,{}!@#$%^&*-+=|/\\<>'`~"));;
91 m_bIconLFs
= CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
92 m_nSelBlockStart
= -1;
95 m_bOtherDiffChecked
= false;
96 m_bInlineWordDiff
= true;
97 m_nTabSize
= (int)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
98 for (int i
=0; i
<MAXFONTS
; i
++)
102 m_hConflictedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDLINE
),
103 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
104 m_hConflictedIgnoredIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDIGNOREDLINE
),
105 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
106 m_hRemovedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_REMOVEDLINE
),
107 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
108 m_hAddedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ADDEDLINE
),
109 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
110 m_hWhitespaceBlockIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WHITESPACELINE
),
111 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
112 m_hEqualIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EQUALLINE
),
113 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
114 m_hLineEndingCR
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCR
),
115 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
116 m_hLineEndingCRLF
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCRLF
),
117 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
118 m_hLineEndingLF
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGLF
),
119 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
120 m_hEditedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEEDITED
),
121 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
122 for (int i
=0; i
<1024; ++i
)
123 m_sConflictedText
+= _T("??");
124 m_sNoLineNr
.LoadString(IDS_EMPTYLINETT
);
128 CBaseView::~CBaseView()
132 m_pCacheBitmap
->DeleteObject();
133 delete m_pCacheBitmap
;
135 for (int i
=0; i
<MAXFONTS
; i
++)
137 if (m_apFonts
[i
] != NULL
)
139 m_apFonts
[i
]->DeleteObject();
144 DestroyIcon(m_hAddedIcon
);
145 DestroyIcon(m_hRemovedIcon
);
146 DestroyIcon(m_hConflictedIcon
);
147 DestroyIcon(m_hConflictedIgnoredIcon
);
148 DestroyIcon(m_hWhitespaceBlockIcon
);
149 DestroyIcon(m_hEqualIcon
);
150 DestroyIcon(m_hLineEndingCR
);
151 DestroyIcon(m_hLineEndingCRLF
);
152 DestroyIcon(m_hLineEndingLF
);
153 DestroyIcon(m_hEditedIcon
);
156 BEGIN_MESSAGE_MAP(CBaseView
, CView
)
168 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE
, OnMergeNextdifference
)
169 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE
, OnMergePreviousdifference
)
170 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
171 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
174 ON_COMMAND(ID_EDIT_COPY
, OnEditCopy
)
176 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT
, OnMergePreviousconflict
)
177 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT
, OnMergeNextconflict
)
179 ON_COMMAND(ID_CARET_DOWN
, &CBaseView::OnCaretDown
)
180 ON_COMMAND(ID_CARET_LEFT
, &CBaseView::OnCaretLeft
)
181 ON_COMMAND(ID_CARET_RIGHT
, &CBaseView::OnCaretRight
)
182 ON_COMMAND(ID_CARET_UP
, &CBaseView::OnCaretUp
)
183 ON_COMMAND(ID_CARET_WORDLEFT
, &CBaseView::OnCaretWordleft
)
184 ON_COMMAND(ID_CARET_WORDRIGHT
, &CBaseView::OnCaretWordright
)
185 ON_COMMAND(ID_EDIT_CUT
, &CBaseView::OnEditCut
)
186 ON_COMMAND(ID_EDIT_PASTE
, &CBaseView::OnEditPaste
)
189 ON_COMMAND(ID_EDIT_SELECTALL
, &CBaseView::OnEditSelectall
)
193 void CBaseView::DocumentUpdated()
195 if (m_pCacheBitmap
!= NULL
)
197 m_pCacheBitmap
->DeleteObject();
198 delete m_pCacheBitmap
;
199 m_pCacheBitmap
= NULL
;
204 m_nMaxLineLength
= -1;
208 m_bOtherDiffChecked
= false;
212 m_nTabSize
= (int)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
213 m_bViewLinenumbers
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
214 m_bShowInlineDiff
= CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE
);
215 m_InlineAddedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR
);
216 m_InlineRemovedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR
);
217 m_ModifiedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR
);
218 m_WhiteSpaceFg
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT
));
219 m_bIconLFs
= CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
220 for (int i
=0; i
<MAXFONTS
; i
++)
222 if (m_apFonts
[i
] != NULL
)
224 m_apFonts
[i
]->DeleteObject();
229 m_nSelBlockStart
= -1;
231 RecalcVertScrollBar();
232 RecalcHorzScrollBar();
237 void CBaseView::UpdateStatusBar()
239 int nRemovedLines
= 0;
241 int nConflictedLines
= 0;
245 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
247 DiffStates state
= m_pViewData
->GetState(i
);
250 case DIFFSTATE_ADDED
:
251 case DIFFSTATE_IDENTICALADDED
:
252 case DIFFSTATE_THEIRSADDED
:
253 case DIFFSTATE_YOURSADDED
:
254 case DIFFSTATE_CONFLICTADDED
:
257 case DIFFSTATE_IDENTICALREMOVED
:
258 case DIFFSTATE_REMOVED
:
259 case DIFFSTATE_THEIRSREMOVED
:
260 case DIFFSTATE_YOURSREMOVED
:
263 case DIFFSTATE_CONFLICTED
:
264 case DIFFSTATE_CONFLICTED_IGNORED
:
276 case CFileTextLines::ASCII
:
277 sBarText
= _T("ASCII ");
279 case CFileTextLines::BINARY
:
280 sBarText
= _T("BINARY ");
282 case CFileTextLines::UNICODE_LE
:
283 sBarText
= _T("UTF-16LE ");
285 case CFileTextLines::UTF8
:
286 sBarText
= _T("UTF8 ");
288 case CFileTextLines::UTF8BOM
:
289 sBarText
= _T("UTF8 BOM ");
296 sBarText
+= _T("LF ");
299 sBarText
+= _T("CRLF ");
302 sBarText
+= _T("LFCR ");
305 sBarText
+= _T("CR ");
309 if (sBarText
.IsEmpty())
310 sBarText
+= _T(" / ");
314 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
315 if (!sBarText
.IsEmpty())
316 sBarText
+= _T(" / ");
321 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
322 if (!sBarText
.IsEmpty())
323 sBarText
+= _T(" / ");
326 if (nConflictedLines
)
328 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
329 if (!sBarText
.IsEmpty())
330 sBarText
+= _T(" / ");
338 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
339 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
341 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
343 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
345 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
346 sBarText
= sTemp
+sBarText
;
348 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
350 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
351 sBarText
= sTemp
+sBarText
;
353 m_pwndStatusBar
->GetPaneInfo(nIndex
, nID
, nStyle
, cxWidth
);
354 //calculate the width of the text
355 CDC
* pDC
= m_pwndStatusBar
->GetDC();
358 CSize size
= pDC
->GetTextExtent(sBarText
);
359 m_pwndStatusBar
->SetPaneInfo(nIndex
, nID
, nStyle
, size
.cx
+2);
362 m_pwndStatusBar
->SetPaneText(nIndex
, sBarText
);
366 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
368 if (!CView::PreCreateWindow(cs
))
371 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
372 cs
.style
&= ~WS_BORDER
;
373 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
374 ::LoadCursor(NULL
, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1), NULL
);
376 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
377 if (pParentWnd
== NULL
|| ! pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
379 // View must always create its own scrollbars,
380 // if only it's not used within splitter
381 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
383 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
387 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/, BOOL bStrikeOut
/*= FALSE*/)
396 if (m_apFonts
[nIndex
] == NULL
)
398 m_apFonts
[nIndex
] = new CFont
;
399 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
400 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
401 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
402 m_lfBaseFont
.lfStrikeOut
= (BYTE
) bStrikeOut
;
404 m_lfBaseFont
.lfStrikeOut
= (BYTE
)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE
);
408 m_lfBaseFont
.lfHeight
= -MulDiv((DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC
->m_hDC
, LOGPIXELSY
), 72);
411 _tcsncpy_s(m_lfBaseFont
.lfFaceName
, 32, (LPCTSTR
)(CString
)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
412 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
414 delete m_apFonts
[nIndex
];
415 m_apFonts
[nIndex
] = NULL
;
416 return CView::GetFont();
419 return m_apFonts
[nIndex
];
422 void CBaseView::CalcLineCharDim()
425 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
426 CSize szCharExt
= pDC
->GetTextExtent(_T("X"));
427 m_nLineHeight
= szCharExt
.cy
;
428 if (m_nLineHeight
<= 0)
430 m_nCharWidth
= szCharExt
.cx
;
431 if (m_nCharWidth
<= 0)
433 pDC
->SelectObject(pOldFont
);
437 int CBaseView::GetScreenChars()
439 if (m_nScreenChars
== -1)
442 GetClientRect(&rect
);
443 m_nScreenChars
= (rect
.Width() - GetMarginWidth()) / GetCharWidth();
445 return m_nScreenChars
;
448 int CBaseView::GetAllMinScreenChars() const
451 if (IsLeftViewGood())
452 nChars
= m_pwndLeft
->GetScreenChars();
453 if (IsRightViewGood())
454 nChars
= (nChars
< m_pwndRight
->GetScreenChars() ? nChars
: m_pwndRight
->GetScreenChars());
455 if (IsBottomViewGood())
456 nChars
= (nChars
< m_pwndBottom
->GetScreenChars() ? nChars
: m_pwndBottom
->GetScreenChars());
460 int CBaseView::GetAllMaxLineLength() const
463 if (IsLeftViewGood())
464 nLength
= m_pwndLeft
->GetMaxLineLength();
465 if (IsRightViewGood())
466 nLength
= (nLength
> m_pwndRight
->GetMaxLineLength() ? nLength
: m_pwndRight
->GetMaxLineLength());
467 if (IsBottomViewGood())
468 nLength
= (nLength
> m_pwndBottom
->GetMaxLineLength() ? nLength
: m_pwndBottom
->GetMaxLineLength());
472 int CBaseView::GetLineHeight()
474 if (m_nLineHeight
== -1)
476 if (m_nLineHeight
<= 0)
478 return m_nLineHeight
;
481 int CBaseView::GetCharWidth()
483 if (m_nCharWidth
== -1)
485 if (m_nCharWidth
<= 0)
490 int CBaseView::GetMaxLineLength()
492 if (m_nMaxLineLength
== -1)
494 m_nMaxLineLength
= 0;
495 int nLineCount
= GetLineCount();
496 for (int i
=0; i
<nLineCount
; i
++)
498 int nActualLength
= GetLineActualLength(i
);
499 if (m_nMaxLineLength
< nActualLength
)
500 m_nMaxLineLength
= nActualLength
;
503 return m_nMaxLineLength
;
506 int CBaseView::GetLineActualLength(int index
) const
508 if (m_pViewData
== NULL
)
511 return CalculateActualOffset(index
, GetLineLength(index
));
514 int CBaseView::GetLineLength(int index
) const
516 if (m_pViewData
== NULL
)
518 if (m_pViewData
->GetCount() == 0)
520 int nLineLength
= m_pViewData
->GetLine(index
).GetLength();
521 ASSERT(nLineLength
>= 0);
525 int CBaseView::GetLineCount() const
527 if (m_pViewData
== NULL
)
529 int nLineCount
= m_pViewData
->GetCount();
530 ASSERT(nLineCount
>= 0);
534 LPCTSTR
CBaseView::GetLineChars(int index
) const
536 if (m_pViewData
== NULL
)
538 if (m_pViewData
->GetCount() == 0)
540 return m_pViewData
->GetLine(index
);
543 void CBaseView::CheckOtherView()
545 if (m_bOtherDiffChecked
)
547 // find out what the 'other' file is
548 m_pOtherViewData
= NULL
;
549 if (this == m_pwndLeft
&& IsRightViewGood())
550 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
552 if (this == m_pwndRight
&& IsLeftViewGood())
553 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
555 m_bOtherDiffChecked
= true;
558 CString
CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
)
560 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
563 DiffStates origstate
= viewData
->GetState(nLineIndex
);
565 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
566 int nStartBlock
= nLineIndex
;
567 int nEndBlock
= nLineIndex
;
568 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
570 DiffStates state
= viewData
->GetState(nStartBlock
- 1);
571 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
573 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
578 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
580 DiffStates state
= viewData
->GetState(nEndBlock
+ 1);
581 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
583 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
590 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
591 block
+= viewData
->GetLine(i
);
595 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
)
597 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
599 if (!m_pOtherViewData
)
602 (m_pViewData
->GetState(nLineIndex
) == DIFFSTATE_NORMAL
) &&
603 (m_pOtherViewData
->GetLine(nLineIndex
) == m_pViewData
->GetLine(nLineIndex
))
607 CString mine
= GetWhitespaceBlock(m_pViewData
, nLineIndex
);
608 CString other
= GetWhitespaceBlock(m_pOtherViewData
, min(nLineIndex
, m_pOtherViewData
->GetCount() - 1));
609 bIdentical
= mine
== other
;
620 return (mine
== other
) && (!mine
.IsEmpty());
623 int CBaseView::GetLineNumber(int index
) const
625 if (m_pViewData
== NULL
)
627 if (m_pViewData
->GetLineNumber(index
)==DIFF_EMPTYLINENUMBER
)
629 return m_pViewData
->GetLineNumber(index
);
632 int CBaseView::GetScreenLines()
634 if (m_nScreenLines
== -1)
637 sbi
.cbSize
= sizeof(sbi
);
638 GetScrollBarInfo(OBJID_HSCROLL
, &sbi
);
639 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
642 GetClientRect(&rect
);
643 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
645 return m_nScreenLines
;
648 int CBaseView::GetAllMinScreenLines() const
651 if (IsLeftViewGood())
652 nLines
= m_pwndLeft
->GetScreenLines();
653 if (IsRightViewGood())
654 nLines
= (nLines
< m_pwndRight
->GetScreenLines() ? nLines
: m_pwndRight
->GetScreenLines());
655 if (IsBottomViewGood())
656 nLines
= (nLines
< m_pwndBottom
->GetScreenLines() ? nLines
: m_pwndBottom
->GetScreenLines());
660 int CBaseView::GetAllLineCount() const
663 if (IsLeftViewGood())
664 nLines
= m_pwndLeft
->GetLineCount();
665 if (IsRightViewGood())
666 nLines
= (nLines
> m_pwndRight
->GetLineCount() ? nLines
: m_pwndRight
->GetLineCount());
667 if (IsBottomViewGood())
668 nLines
= (nLines
> m_pwndBottom
->GetLineCount() ? nLines
: m_pwndBottom
->GetLineCount());
672 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
674 if (IsLeftViewGood())
675 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
676 if (IsRightViewGood())
677 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
678 if (IsBottomViewGood())
679 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
682 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
685 si
.cbSize
= sizeof(si
);
689 si
.nPos
= m_nTopLine
;
693 EnableScrollBarCtrl(SB_VERT
, TRUE
);
694 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
699 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
701 si
.nMax
= GetAllLineCount();
702 si
.nPage
= GetAllMinScreenLines();
703 si
.nPos
= m_nTopLine
;
705 VERIFY(SetScrollInfo(SB_VERT
, &si
));
708 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
710 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
712 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
714 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
716 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
718 m_pwndLocator
->Invalidate();
721 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
723 // Note we cannot use nPos because of its 16-bit nature
725 si
.cbSize
= sizeof(si
);
727 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
729 int nPageLines
= GetScreenLines();
730 int nLineCount
= GetLineCount();
736 static LONG textwidth
= 0;
737 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
744 nNewTopLine
= nLineCount
- nPageLines
+ 1;
747 nNewTopLine
= m_nTopLine
- 1;
750 nNewTopLine
= m_nTopLine
+ 1;
753 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
756 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
758 case SB_THUMBPOSITION
:
759 m_ScrollTool
.Clear();
760 nNewTopLine
= si
.nTrackPos
;
764 nNewTopLine
= si
.nTrackPos
;
765 if (GetFocus() == this)
767 GetClientRect(&thumbrect
);
768 ClientToScreen(&thumbrect
);
769 thumbpoint
.x
= thumbrect
.right
;
770 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
771 m_ScrollTool
.Init(&thumbpoint
);
774 CString sTemp
= sFormat
;
775 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
776 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
778 thumbpoint
.x
-= textwidth
;
779 int line
= GetLineNumber(nNewTopLine
);
781 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
783 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
792 if (nNewTopLine
>= nLineCount
)
793 nNewTopLine
= nLineCount
- 1;
794 ScrollToLine(nNewTopLine
);
797 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
799 if (IsLeftViewGood())
800 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
801 if (IsRightViewGood())
802 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
803 if (IsBottomViewGood())
804 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
807 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
810 si
.cbSize
= sizeof(si
);
814 si
.nPos
= m_nOffsetChar
;
818 EnableScrollBarCtrl(SB_HORZ
, TRUE
);
819 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar
> 0)
824 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
826 si
.nMax
= GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
827 si
.nPage
= GetAllMinScreenChars();
828 si
.nPos
= m_nOffsetChar
;
830 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
833 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
835 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
837 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
839 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
841 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
843 m_pwndLocator
->Invalidate();
846 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
849 si
.cbSize
= sizeof(si
);
851 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
853 int nPageChars
= GetScreenChars();
854 int nMaxLineLength
= GetMaxLineLength();
863 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
866 nNewOffset
= m_nOffsetChar
- 1;
869 nNewOffset
= m_nOffsetChar
+ 1;
872 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
875 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
877 case SB_THUMBPOSITION
:
879 nNewOffset
= si
.nTrackPos
;
885 if (nNewOffset
>= nMaxLineLength
)
886 nNewOffset
= nMaxLineLength
- 1;
889 ScrollToChar(nNewOffset
, TRUE
);
892 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
894 if (m_nOffsetChar
!= nNewOffsetChar
)
896 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
897 m_nOffsetChar
= nNewOffsetChar
;
899 GetClientRect(&rcScroll
);
900 rcScroll
.left
+= GetMarginWidth();
901 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
902 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
903 // update the view header
906 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
907 InvalidateRect(&rcScroll
, FALSE
);
910 RecalcHorzScrollBar(TRUE
);
915 void CBaseView::ScrollSide(int delta
)
917 int nNewOffset
= m_nOffsetChar
;
919 int nMaxLineLength
= GetMaxLineLength();
920 if (nNewOffset
>= nMaxLineLength
)
921 nNewOffset
= nMaxLineLength
- 1;
924 ScrollToChar(nNewOffset
, TRUE
);
925 if (m_pwndLineDiffBar
)
926 m_pwndLineDiffBar
->Invalidate();
930 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
932 if (m_nTopLine
!= nNewTopLine
)
936 int nScrollLines
= m_nTopLine
- nNewTopLine
;
937 m_nTopLine
= nNewTopLine
;
939 GetClientRect(&rcScroll
);
940 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
941 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
944 RecalcVertScrollBar(TRUE
);
950 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
952 pdc
->FillSolidRect(rect
, ::GetSysColor(COLOR_SCROLLBAR
));
954 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
956 DiffStates state
= m_pViewData
->GetState(nLineIndex
);
960 case DIFFSTATE_ADDED
:
961 case DIFFSTATE_THEIRSADDED
:
962 case DIFFSTATE_YOURSADDED
:
963 case DIFFSTATE_IDENTICALADDED
:
964 case DIFFSTATE_CONFLICTADDED
:
967 case DIFFSTATE_REMOVED
:
968 case DIFFSTATE_THEIRSREMOVED
:
969 case DIFFSTATE_YOURSREMOVED
:
970 case DIFFSTATE_IDENTICALREMOVED
:
971 icon
= m_hRemovedIcon
;
973 case DIFFSTATE_CONFLICTED
:
974 icon
= m_hConflictedIcon
;
976 case DIFFSTATE_CONFLICTED_IGNORED
:
977 icon
= m_hConflictedIgnoredIcon
;
979 case DIFFSTATE_EDITED
:
980 icon
= m_hEditedIcon
;
985 bool bIdentical
= false;
986 if ((state
!= DIFFSTATE_EDITED
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
)))
991 icon
= m_hWhitespaceBlockIcon
;
996 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ 2, rect
.top
+ (rect
.Height()-16)/2, icon
, 16, 16, NULL
, NULL
, DI_NORMAL
);
998 if ((m_bViewLinenumbers
)&&(m_nDigits
))
1000 int nLineNumber
= GetLineNumber(nLineIndex
);
1001 if (nLineNumber
>= 0)
1003 CString sLinenumberFormat
;
1004 CString sLinenumber
;
1005 sLinenumberFormat
.Format(_T("%%%dd"), m_nDigits
);
1006 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1007 pdc
->SetBkColor(::GetSysColor(COLOR_SCROLLBAR
));
1008 pdc
->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT
));
1010 pdc
->SelectObject(GetFont());
1011 pdc
->ExtTextOut(rect
.left
+ 18, rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, NULL
);
1017 int CBaseView::GetMarginWidth()
1019 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1021 int nWidth
= GetCharWidth();
1024 int nLength
= (int)m_pViewData
->GetCount();
1025 // find out how many digits are needed to show the highest line number
1027 while (nLength
/ 10)
1032 m_nDigits
= nDigits
;
1034 return (MARGINWIDTH
+ (m_nDigits
* nWidth
) + 2);
1039 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1041 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1042 COLORREF crBk
, crFg
;
1043 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL
, crBk
, crFg
);
1044 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
1045 if (IsBottomViewGood())
1047 pdc
->SetBkColor(crBk
);
1052 if (this == m_pwndRight
)
1054 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED
, crBk
, crFg
);
1055 pdc
->SetBkColor(crBk
);
1059 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED
, crBk
, crFg
);
1060 pdc
->SetBkColor(crBk
);
1063 pdc
->FillSolidRect(textrect
, crBk
);
1065 pdc
->SetTextColor(crFg
);
1067 pdc
->SelectObject(GetFont(FALSE
, TRUE
, FALSE
));
1070 if (m_sWindowName
.Left(2).Compare(_T("* "))!=0)
1071 m_sWindowName
= _T("* ") + m_sWindowName
;
1075 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
1076 m_sWindowName
= m_sWindowName
.Mid(2);
1078 CString sViewTitle
= m_sWindowName
;
1079 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1080 if (nStringLength
> rect
.Width())
1082 int offset
= min(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1084 sViewTitle
= m_sWindowName
.Mid(offset
);
1086 pdc
->ExtTextOut(max(rect
.left
+ (rect
.Width()-nStringLength
)/2, 1),
1087 rect
.top
+(HEADERHEIGHT
/2), ETO_CLIPPED
, textrect
, sViewTitle
, NULL
);
1088 if (this->GetFocus() == this)
1089 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1091 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1094 void CBaseView::OnDraw(CDC
* pDC
)
1097 GetClientRect(rcClient
);
1099 int nLineCount
= GetLineCount();
1100 int nLineHeight
= GetLineHeight();
1103 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1104 if (m_pCacheBitmap
== NULL
)
1106 m_pCacheBitmap
= new CBitmap
;
1107 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1109 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1111 DrawHeader(pDC
, rcClient
);
1115 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1116 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1117 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1118 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1120 int nCurrentLine
= m_nTopLine
;
1121 while (rcLine
.top
< rcClient
.bottom
)
1123 if (nCurrentLine
< nLineCount
)
1125 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1126 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1130 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1131 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1134 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1137 rcLine
.OffsetRect(0, nLineHeight
);
1140 cacheDC
.SelectObject(pOldBitmap
);
1144 BOOL
CBaseView::IsLineRemoved(int nLineIndex
)
1146 DiffStates state
= DIFFSTATE_UNKNOWN
;
1148 state
= m_pViewData
->GetState(nLineIndex
);
1152 case DIFFSTATE_REMOVED
:
1153 case DIFFSTATE_THEIRSREMOVED
:
1154 case DIFFSTATE_YOURSREMOVED
:
1155 case DIFFSTATE_IDENTICALREMOVED
:
1165 bool CBaseView::IsLineConflicted(int nLineIndex
)
1167 DiffStates state
= DIFFSTATE_UNKNOWN
;
1169 state
= m_pViewData
->GetState(nLineIndex
);
1173 case DIFFSTATE_CONFLICTED
:
1174 case DIFFSTATE_CONFLICTED_IGNORED
:
1175 case DIFFSTATE_CONFLICTEMPTY
:
1176 case DIFFSTATE_CONFLICTADDED
:
1186 COLORREF
CBaseView::IntenseColor(long scale
, COLORREF col
)
1188 // if the color is already dark (gray scale below 127),
1189 // then lighten the color by 'scale', otherwise darken it
1190 int Gray
= (((int)GetRValue(col
)) + GetGValue(col
) + GetBValue(col
))/3;
1193 long red
= MulDiv(GetRValue(col
),(255-scale
),255);
1194 long green
= MulDiv(GetGValue(col
),(255-scale
),255);
1195 long blue
= MulDiv(GetBValue(col
),(255-scale
),255);
1197 return RGB(red
, green
, blue
);
1199 long R
= MulDiv(255-GetRValue(col
),scale
,255)+GetRValue(col
);
1200 long G
= MulDiv(255-GetGValue(col
),scale
,255)+GetGValue(col
);
1201 long B
= MulDiv(255-GetBValue(col
),scale
,255)+GetBValue(col
);
1203 return RGB(R
, G
, B
);
1206 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1208 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1211 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1213 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< m_pViewData
->GetCount())))
1216 EOL ending
= m_pViewData
->GetLineEnding(nLineIndex
);
1219 HICON hEndingIcon
= NULL
;
1222 case EOL_CR
: hEndingIcon
= m_hLineEndingCR
; break;
1223 case EOL_CRLF
: hEndingIcon
= m_hLineEndingCRLF
; break;
1224 case EOL_LF
: hEndingIcon
= m_hLineEndingLF
; break;
1227 if (origin
.x
< (rc
.left
-GetCharWidth()))
1229 // If EOL style has changed, color end-of-line markers as inline differences.
1231 m_bShowInlineDiff
&& m_pOtherViewData
&&
1232 (nLineIndex
< m_pOtherViewData
->GetCount()) &&
1233 (ending
!= EOL_NOENDING
) &&
1234 (ending
!= m_pOtherViewData
->GetLineEnding(nLineIndex
) &&
1235 (m_pOtherViewData
->GetLineEnding(nLineIndex
) != EOL_NOENDING
))
1238 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1241 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), NULL
, NULL
, DI_NORMAL
);
1245 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1246 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1247 int yMiddle
= origin
.y
+ rc
.Height()/2;
1248 int xMiddle
= origin
.x
+GetCharWidth()/2;
1252 // arrow from right to left
1253 pDC
->MoveTo(origin
.x
+GetCharWidth(), yMiddle
);
1254 pDC
->LineTo(origin
.x
, yMiddle
);
1255 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1256 pDC
->MoveTo(origin
.x
, yMiddle
);
1257 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1260 // arrow from top to middle+2, then left
1261 pDC
->MoveTo(origin
.x
+GetCharWidth(), rc
.top
);
1262 pDC
->LineTo(origin
.x
+GetCharWidth(), yMiddle
);
1263 pDC
->LineTo(origin
.x
, yMiddle
);
1264 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1265 pDC
->MoveTo(origin
.x
, yMiddle
);
1266 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1269 // arrow from top to bottom
1270 pDC
->MoveTo(xMiddle
, rc
.top
);
1271 pDC
->LineTo(xMiddle
, rc
.bottom
-1);
1272 pDC
->LineTo(xMiddle
+4, rc
.bottom
-5);
1273 pDC
->MoveTo(xMiddle
, rc
.bottom
-1);
1274 pDC
->LineTo(xMiddle
-4, rc
.bottom
-5);
1277 pDC
->SelectObject(oldpen
);
1281 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1283 const int THICKNESS
= 2;
1284 COLORREF rectcol
= GetSysColor(m_bFocused
? COLOR_WINDOWTEXT
: COLOR_GRAYTEXT
);
1285 if ((nLineIndex
== m_nSelBlockStart
) && m_bShowSelection
)
1287 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1289 if ((nLineIndex
== m_nSelBlockEnd
) && m_bShowSelection
)
1291 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1295 void CBaseView::DrawText(
1296 CDC
* pDC
, const CRect
&rc
, LPCTSTR text
, int textlength
, int nLineIndex
, POINT coords
, bool bModified
, bool bInlineDiff
)
1298 ASSERT(m_pViewData
&& (nLineIndex
< m_pViewData
->GetCount()));
1299 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1301 // first suppose the whole line is selected
1302 int selectedStart
= 0, selectedEnd
= textlength
;
1304 if ((m_ptSelectionStartPos
.y
> nLineIndex
) || (m_ptSelectionEndPos
.y
< nLineIndex
)
1305 || ! m_bShowSelection
)
1307 // this line has no selected text
1308 selectedStart
= textlength
;
1310 else if ((m_ptSelectionStartPos
.y
== nLineIndex
) || (m_ptSelectionEndPos
.y
== nLineIndex
))
1312 // the line is partially selected
1313 int xoffs
= m_nOffsetChar
+ (coords
.x
- GetMarginWidth()) / GetCharWidth();
1314 if (m_ptSelectionStartPos
.y
== nLineIndex
)
1316 // the first line of selection
1317 int nSelectionStartOffset
= CalculateActualOffset(m_ptSelectionStartPos
.y
, m_ptSelectionStartPos
.x
);
1318 selectedStart
= max(min(nSelectionStartOffset
- xoffs
, textlength
), 0);
1321 if (m_ptSelectionEndPos
.y
== nLineIndex
)
1323 // the last line of selection
1324 int nSelectionEndOffset
= CalculateActualOffset(m_ptSelectionEndPos
.y
, m_ptSelectionEndPos
.x
);
1325 selectedEnd
= max(min(nSelectionEndOffset
- xoffs
, textlength
), 0);
1329 COLORREF crBkgnd
, crText
;
1330 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1331 if (bModified
|| (diffState
== DIFFSTATE_EDITED
))
1332 crBkgnd
= m_ModifiedBk
;
1334 crBkgnd
= InlineDiffColor(nLineIndex
);
1336 pDC
->SetBkColor(crBkgnd
);
1337 pDC
->SetTextColor(crText
);
1338 if (selectedStart
>=0)
1339 VERIFY(pDC
->ExtTextOut(coords
.x
, coords
.y
, ETO_CLIPPED
, &rc
, text
, selectedStart
, NULL
));
1341 long intenseColorScale
= m_bFocused
? 70 : 30;
1342 pDC
->SetBkColor(IntenseColor(intenseColorScale
, crBkgnd
));
1343 pDC
->SetTextColor(IntenseColor(intenseColorScale
, crText
));
1344 VERIFY(pDC
->ExtTextOut(
1345 coords
.x
+ selectedStart
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1346 text
+ selectedStart
, selectedEnd
- selectedStart
, NULL
));
1348 pDC
->SetBkColor(crBkgnd
);
1349 pDC
->SetTextColor(crText
);
1350 if (textlength
- selectedEnd
>= 0)
1351 VERIFY(pDC
->ExtTextOut(
1352 coords
.x
+ selectedEnd
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1353 text
+ selectedEnd
, textlength
- selectedEnd
, NULL
));
1356 bool CBaseView::DrawInlineDiff(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CString
&line
, CPoint
&origin
)
1358 if (!m_bShowInlineDiff
|| line
.IsEmpty())
1360 if ((m_pwndBottom
!= NULL
) && !(m_pwndBottom
->IsHidden()))
1363 LPCTSTR pszDiffChars
= NULL
;
1364 int nDiffLength
= 0;
1365 if (m_pOtherViewData
)
1367 int index
= min(nLineIndex
, m_pOtherViewData
->GetCount() - 1);
1368 pszDiffChars
= m_pOtherViewData
->GetLine(index
);
1369 nDiffLength
= m_pOtherViewData
->GetLine(index
).GetLength();
1372 if (!pszDiffChars
|| !*pszDiffChars
)
1376 ExpandChars(pszDiffChars
, 0, nDiffLength
, diffline
);
1377 svn_diff_t
* diff
= NULL
;
1378 m_svnlinediff
.Diff(&diff
, line
, line
.GetLength(), diffline
, diffline
.GetLength(), m_bInlineWordDiff
);
1379 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
1383 std::deque
<int> removedPositions
;
1386 apr_off_t len
= diff
->original_length
;
1389 for (int i
= 0; i
< len
; ++i
)
1391 s
+= m_svnlinediff
.m_line1tokens
[lineoffset
].c_str();
1394 bool isModified
= diff
->type
== svn_diff__type_diff_modified
;
1395 DrawText(pDC
, rc
, (LPCTSTR
)s
, s
.GetLength(), nLineIndex
, origin
, true, isModified
);
1396 origin
.x
+= pDC
->GetTextExtent(s
).cx
;
1398 if (isModified
&& (len
< diff
->modified_length
))
1399 removedPositions
.push_back(origin
.x
- 1);
1403 // Draw vertical bars at removed chunks' positions.
1404 for (std::deque
<int>::iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
1405 pDC
->FillSolidRect(*it
, rc
.top
, 1, rc
.Height(), m_InlineRemovedBk
);
1409 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1411 if (nLineIndex
>= GetLineCount())
1413 ASSERT(nLineIndex
>= -1);
1415 if ((nLineIndex
== -1) || !m_pViewData
)
1417 // Draw line beyond the text
1418 COLORREF crBkgnd
, crText
;
1419 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1420 pDC
->FillSolidRect(rc
, crBkgnd
);
1424 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1425 COLORREF crBkgnd
, crText
;
1426 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1428 if (diffState
== DIFFSTATE_CONFLICTED
)
1430 // conflicted lines are shown without 'text' on them
1432 pDC
->FillSolidRect(rc
, crBkgnd
);
1433 // now draw some faint text patterns
1434 pDC
->SetTextColor(IntenseColor(130, crBkgnd
));
1435 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
1436 DrawBlockLine(pDC
, rc
, nLineIndex
);
1440 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
1441 int nLength
= GetLineLength(nLineIndex
);
1444 // Draw the empty line
1445 pDC
->FillSolidRect(rc
, crBkgnd
);
1446 DrawBlockLine(pDC
, rc
, nLineIndex
);
1447 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1450 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
1451 if (pszChars
== NULL
)
1458 pDC
->SelectObject(GetFont(FALSE
, FALSE
, IsLineRemoved(nLineIndex
)));
1460 ExpandChars(pszChars
, 0, nLength
, line
);
1462 int nWidth
= rc
.right
- origin
.x
;
1463 int savedx
= origin
.x
;
1464 bool bInlineDiffDrawn
=
1465 nWidth
> 0 && diffState
!= DIFFSTATE_NORMAL
&&
1466 DrawInlineDiff(pDC
, rc
, nLineIndex
, line
, origin
);
1468 if (!bInlineDiffDrawn
)
1470 int nCount
= min(line
.GetLength(), nWidth
/ GetCharWidth() + 1);
1471 DrawText(pDC
, rc
, line
, nCount
, nLineIndex
, origin
, false, false);
1474 origin
.x
= savedx
+ pDC
->GetTextExtent(line
).cx
;
1476 // draw white space after the end of line
1478 if (origin
.x
> frect
.left
)
1479 frect
.left
= origin
.x
;
1480 if (bInlineDiffDrawn
)
1481 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1482 if (frect
.right
> frect
.left
)
1483 pDC
->FillSolidRect(frect
, crBkgnd
);
1484 // draw the whitespace chars
1485 if (m_bViewWhitespace
)
1488 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
1490 int nActualOffset
= 0;
1491 while ((nActualOffset
< m_nOffsetChar
) && (*pszChars
))
1493 if (*pszChars
== _T('\t'))
1494 nActualOffset
+= (GetTabSize() - nActualOffset
% GetTabSize());
1499 if (nActualOffset
> m_nOffsetChar
)
1502 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1503 CPen
pen2(PS_SOLID
, 2, m_WhiteSpaceFg
);
1511 CPen
* oldPen
= pDC
->SelectObject(&pen
);
1512 int nSpaces
= GetTabSize() - (m_nOffsetChar
+ xpos
) % GetTabSize();
1513 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
, y
);
1514 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1515 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
-4);
1516 pDC
->MoveTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1517 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
+4);
1519 pDC
->SelectObject(oldPen
);
1525 CPen
* oldPen
= pDC
->SelectObject(&pen2
);
1526 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2-1, y
);
1527 pDC
->LineTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2+1, y
);
1529 pDC
->SelectObject(oldPen
);
1539 DrawBlockLine(pDC
, rc
, nLineIndex
);
1540 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1543 void CBaseView::ExpandChars(LPCTSTR pszChars
, int nOffset
, int nCount
, CString
&line
)
1551 int nTabSize
= GetTabSize();
1553 int nActualOffset
= 0;
1554 for (int i
=0; i
<nOffset
; i
++)
1556 if (pszChars
[i
] == _T('\t'))
1557 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
1562 pszChars
+= nOffset
;
1563 int nLength
= nCount
;
1566 for (int i
=0; i
<nLength
; i
++)
1568 if (pszChars
[i
] == _T('\t'))
1572 LPTSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
1574 if (nTabCount
> 0 || m_bViewWhitespace
)
1576 for (int i
=0; i
<nLength
; i
++)
1578 if (pszChars
[i
] == _T('\t'))
1580 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
1583 pszBuf
[nCurPos
++] = _T(' ');
1589 pszBuf
[nCurPos
] = pszChars
[i
];
1596 memcpy(pszBuf
, pszChars
, sizeof(TCHAR
) * nLength
);
1599 pszBuf
[nCurPos
] = 0;
1600 line
.ReleaseBuffer();
1603 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
1605 if ((m_pwndLeft
)&&(m_pwndRight
))
1607 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1608 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1613 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1615 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1618 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1620 m_pwndLocator
->Invalidate();
1623 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
1625 //almost the same as ScrollAllToLine, but try to put the line in the
1626 //middle of the view, not on top
1627 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
1628 if (nNewTopLine
< 0)
1632 if (nNewTopLine
>= m_pViewData
->GetCount())
1633 nNewTopLine
= m_pViewData
->GetCount()-1;
1635 ScrollAllToLine(nNewTopLine
);
1637 ScrollToLine(nNewTopLine
);
1641 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
1646 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
1648 if (CView::OnCreate(lpCreateStruct
) == -1)
1651 memset(&m_lfBaseFont
, 0, sizeof(m_lfBaseFont
));
1652 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1653 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1654 m_lfBaseFont
.lfHeight
= 0;
1655 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
1656 m_lfBaseFont
.lfItalic
= FALSE
;
1657 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
1658 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1659 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1660 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
1661 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
1666 void CBaseView::OnDestroy()
1669 for (int i
=0; i
<MAXFONTS
; i
++)
1671 if (m_apFonts
[i
] != NULL
)
1673 m_apFonts
[i
]->DeleteObject();
1674 delete m_apFonts
[i
];
1675 m_apFonts
[i
] = NULL
;
1678 if (m_pCacheBitmap
!= NULL
)
1680 delete m_pCacheBitmap
;
1681 m_pCacheBitmap
= NULL
;
1685 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
1687 if (m_pCacheBitmap
!= NULL
)
1689 m_pCacheBitmap
->DeleteObject();
1690 delete m_pCacheBitmap
;
1691 m_pCacheBitmap
= NULL
;
1693 // make sure the view header is redrawn
1695 GetClientRect(&rcScroll
);
1696 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1697 InvalidateRect(&rcScroll
, FALSE
);
1699 m_nScreenLines
= -1;
1700 m_nScreenChars
= -1;
1701 RecalcVertScrollBar();
1702 RecalcHorzScrollBar();
1703 CView::OnSize(nType
, cx
, cy
);
1706 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
1709 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1711 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1713 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1715 m_pwndLocator
->Invalidate();
1716 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
1719 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
1721 if (GetKeyState(VK_CONTROL
)&0x8000)
1723 // Ctrl-Wheel scrolls sideways
1724 ScrollSide(-zDelta
/30);
1728 int nLineCount
= GetLineCount();
1729 int nTopLine
= m_nTopLine
;
1730 nTopLine
-= (zDelta
/30);
1733 if (nTopLine
>= nLineCount
)
1734 nTopLine
= nLineCount
- 1;
1735 ScrollToLine(nTopLine
, TRUE
);
1739 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1741 if (nHitTest
== HTCLIENT
)
1743 ::SetCursor(::LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
))); // Set To Arrow Cursor
1746 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
1749 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
1751 CView::OnKillFocus(pNewWnd
);
1757 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
1759 CView::OnSetFocus(pOldWnd
);
1765 int CBaseView::GetLineFromPoint(CPoint point
)
1767 ScreenToClient(&point
);
1768 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
1771 bool CBaseView::OnContextMenu(CPoint
/*point*/, int /*nLine*/, DiffStates
/*state*/)
1776 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
1778 int nLine
= GetLineFromPoint(point
);
1782 if (m_nSelBlockEnd
>= GetLineCount())
1783 m_nSelBlockEnd
= GetLineCount()-1;
1784 if ((nLine
<= m_pViewData
->GetCount())&&(nLine
> m_nTopLine
))
1786 int nIndex
= nLine
- 1;
1787 DiffStates state
= m_pViewData
->GetState(nIndex
);
1788 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1790 // if there's nothing selected, or if the selection is outside the window then
1791 // select the diff block under the cursor.
1792 if (((m_nSelBlockStart
<0)&&(m_nSelBlockEnd
<0))||
1793 ((m_nSelBlockEnd
< m_nTopLine
)||(m_nSelBlockStart
> m_nTopLine
+m_nScreenLines
)))
1802 if (state
!= m_pViewData
->GetState(--nIndex
))
1805 m_nSelBlockStart
= nIndex
+1;
1806 while (nIndex
< (m_pViewData
->GetCount()-1))
1808 if (state
!= m_pViewData
->GetState(++nIndex
))
1811 if ((nIndex
== (m_pViewData
->GetCount()-1))&&(state
== m_pViewData
->GetState(nIndex
)))
1812 m_nSelBlockEnd
= nIndex
;
1814 m_nSelBlockEnd
= nIndex
-1;
1815 SetupSelection(m_nSelBlockStart
, m_nSelBlockEnd
);
1817 m_ptCaretPos
.y
= nLine
- 1;
1821 if (((state
== DIFFSTATE_NORMAL
)||(state
== DIFFSTATE_UNKNOWN
)) &&
1822 (m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))
1824 // find a more 'relevant' state in the selection
1825 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; ++i
)
1827 state
= m_pViewData
->GetState(i
);
1828 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1832 bool bKeepSelection
= OnContextMenu(point
, nLine
, state
);
1833 if (! bKeepSelection
)
1839 void CBaseView::RefreshViews()
1843 m_pwndLeft
->UpdateStatusBar();
1844 m_pwndLeft
->Invalidate();
1848 m_pwndRight
->UpdateStatusBar();
1849 m_pwndRight
->Invalidate();
1853 m_pwndBottom
->UpdateStatusBar();
1854 m_pwndBottom
->Invalidate();
1857 m_pwndLocator
->Invalidate();
1860 void CBaseView::GoToFirstDifference()
1863 SelectNextBlock(1, false, false);
1866 void CBaseView::HiglightLines(int start
, int end
/* = -1 */)
1869 m_nSelBlockStart
= start
;
1872 m_nSelBlockEnd
= end
;
1874 m_ptCaretPos
.y
= start
;
1879 void CBaseView::SetupSelection(int start
, int end
)
1881 if (IsBottomViewGood())
1883 m_pwndBottom
->m_nSelBlockStart
= start
;
1884 m_pwndBottom
->m_nSelBlockEnd
= end
;
1885 m_pwndBottom
->Invalidate();
1887 if (IsLeftViewGood())
1889 m_pwndLeft
->m_nSelBlockStart
= start
;
1890 m_pwndLeft
->m_nSelBlockEnd
= end
;
1891 m_pwndLeft
->Invalidate();
1893 if (IsRightViewGood())
1895 m_pwndRight
->m_nSelBlockStart
= start
;
1896 m_pwndRight
->m_nSelBlockEnd
= end
;
1897 m_pwndRight
->Invalidate();
1901 void CBaseView::OnMergePreviousconflict()
1903 SelectNextBlock(-1, true);
1906 void CBaseView::OnMergeNextconflict()
1908 SelectNextBlock(1, true);
1911 void CBaseView::OnMergeNextdifference()
1913 SelectNextBlock(1, false);
1916 void CBaseView::OnMergePreviousdifference()
1918 SelectNextBlock(-1, false);
1921 void CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */)
1926 if (m_pViewData
->GetCount() == 0)
1929 int nCenterPos
= m_ptCaretPos
.y
;
1932 nLimit
= m_pViewData
->GetCount() - 1;
1934 if (nCenterPos
>= m_pViewData
->GetCount())
1935 nCenterPos
= m_pViewData
->GetCount()-1;
1937 if (bSkipEndOfCurrentBlock
)
1939 // Find end of current block
1940 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1941 while ((nCenterPos
!= nLimit
) &&
1942 (m_pViewData
->GetState(nCenterPos
)==state
))
1943 nCenterPos
+= nDirection
;
1946 // Find next diff/conflict block
1947 while (nCenterPos
!= nLimit
)
1949 DiffStates linestate
= m_pViewData
->GetState(nCenterPos
);
1951 (linestate
!= DIFFSTATE_NORMAL
) &&
1952 (linestate
!= DIFFSTATE_UNKNOWN
))
1955 ((linestate
== DIFFSTATE_CONFLICTADDED
) ||
1956 (linestate
== DIFFSTATE_CONFLICTED_IGNORED
) ||
1957 (linestate
== DIFFSTATE_CONFLICTED
) ||
1958 (linestate
== DIFFSTATE_CONFLICTEMPTY
)))
1961 nCenterPos
+= nDirection
;
1964 // Find end of new block
1965 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1966 int nBlockEnd
= nCenterPos
;
1967 while ((nBlockEnd
!= nLimit
) &&
1968 (state
== m_pViewData
->GetState(nBlockEnd
+ nDirection
)))
1969 nBlockEnd
+= nDirection
;
1971 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
1976 m_ptCaretPos
.y
= nCenterPos
;
1979 SetupSelection(nCenterPos
, nBlockEnd
);
1981 SetupSelection(nBlockEnd
, nCenterPos
);
1983 ScrollAllToLine(nTopPos
, FALSE
);
1984 RecalcAllVertScrollBars(TRUE
);
1985 m_nCaretGoalPos
= 0;
1987 ShowDiffLines(nCenterPos
);
1990 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
1992 // need to handle both ANSI and UNICODE versions of the message
1993 TOOLTIPTEXTA
* pTTTA
= (TOOLTIPTEXTA
*)pNMHDR
;
1994 TOOLTIPTEXTW
* pTTTW
= (TOOLTIPTEXTW
*)pNMHDR
;
1996 UINT nID
= (UINT
)pNMHDR
->idFrom
;
1997 if (pNMHDR
->code
== TTN_NEEDTEXTA
&& (pTTTA
->uFlags
& TTF_IDISHWND
) ||
1998 pNMHDR
->code
== TTN_NEEDTEXTW
&& (pTTTW
->uFlags
& TTF_IDISHWND
))
2000 // idFrom is actually the HWND of the tool
2001 nID
= ::GetDlgCtrlID((HWND
)nID
);
2004 if (pNMHDR
->idFrom
== (UINT
)m_hWnd
)
2006 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
2008 strTipText
= m_sWindowName
.Mid(2) + _T("\r\n") + m_sFullFilePath
;
2012 strTipText
= m_sWindowName
+ _T("\r\n") + m_sFullFilePath
;
2019 if (strTipText
.IsEmpty())
2022 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2024 pTTTA
->lpszText
= m_szTip
;
2025 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2029 lstrcpyn(m_wszTip
, strTipText
, strTipText
.GetLength()+1);
2030 pTTTW
->lpszText
= m_wszTip
;
2033 return TRUE
; // message was handled
2037 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2040 GetClientRect(rcClient
);
2041 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2042 if (textrect
.PtInRect(point
))
2044 // inside the header part of the view (showing the filename)
2045 pTI
->hwnd
= this->m_hWnd
;
2046 this->GetClientRect(&pTI
->rect
);
2047 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
2048 pTI
->uId
= (UINT
)m_hWnd
;
2049 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
2051 // we want multi line tooltips
2052 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
2053 if (pToolTip
->GetSafeHwnd() != NULL
)
2055 pToolTip
->SetMaxTipWidth(INT_MAX
);
2063 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2065 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2066 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2071 m_ptCaretPos
.y
-= GetScreenLines();
2072 m_ptCaretPos
.y
= max(m_ptCaretPos
.y
, 0);
2073 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2079 EnsureCaretVisible();
2080 ShowDiffLines(m_ptCaretPos
.y
);
2085 m_ptCaretPos
.y
+= GetScreenLines();
2086 if (m_ptCaretPos
.y
>= GetLineCount())
2087 m_ptCaretPos
.y
= GetLineCount()-1;
2088 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2094 EnsureCaretVisible();
2095 ShowDiffLines(m_ptCaretPos
.y
);
2105 m_nCaretGoalPos
= 0;
2115 m_nCaretGoalPos
= 0;
2120 EnsureCaretVisible();
2129 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2130 m_ptCaretPos
.y
= GetLineCount()-1;
2131 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2141 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2147 EnsureCaretVisible();
2157 if (! HasTextSelection()) {
2158 if (m_ptCaretPos
.y
== 0 && m_ptCaretPos
.x
== 0)
2160 m_ptSelectionEndPos
= m_ptCaretPos
;
2162 m_ptSelectionStartPos
= m_ptCaretPos
;
2164 RemoveSelectedText();
2172 if (! HasTextSelection()) {
2173 if (! MoveCaretRight())
2175 m_ptSelectionEndPos
= m_ptCaretPos
;
2177 m_ptSelectionStartPos
= m_ptCaretPos
;
2179 RemoveSelectedText();
2183 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
2186 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
2188 int nClickedLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2189 nClickedLine
--; //we need the index
2190 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
2192 m_ptCaretPos
.y
= nClickedLine
;
2193 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2196 if (nFlags
& MK_SHIFT
)
2201 SetupSelection(m_ptCaretPos
.y
, m_ptCaretPos
.y
);
2209 CView::OnLButtonDown(nFlags
, point
);
2212 void CBaseView::OnEditCopy()
2214 if ((m_ptSelectionStartPos
.x
== m_ptSelectionEndPos
.x
)&&(m_ptSelectionStartPos
.y
== m_ptSelectionEndPos
.y
))
2216 // first store the selected lines in one CString
2218 for (int i
=m_ptSelectionStartPos
.y
; i
<=m_ptSelectionEndPos
.y
; i
++)
2220 switch (m_pViewData
->GetState(i
))
2222 case DIFFSTATE_EMPTY
:
2224 case DIFFSTATE_UNKNOWN
:
2225 case DIFFSTATE_NORMAL
:
2226 case DIFFSTATE_REMOVED
:
2227 case DIFFSTATE_REMOVEDWHITESPACE
:
2228 case DIFFSTATE_ADDED
:
2229 case DIFFSTATE_ADDEDWHITESPACE
:
2230 case DIFFSTATE_WHITESPACE
:
2231 case DIFFSTATE_WHITESPACE_DIFF
:
2232 case DIFFSTATE_CONFLICTED
:
2233 case DIFFSTATE_CONFLICTED_IGNORED
:
2234 case DIFFSTATE_CONFLICTADDED
:
2235 case DIFFSTATE_CONFLICTEMPTY
:
2236 case DIFFSTATE_CONFLICTRESOLVED
:
2237 case DIFFSTATE_IDENTICALREMOVED
:
2238 case DIFFSTATE_IDENTICALADDED
:
2239 case DIFFSTATE_THEIRSREMOVED
:
2240 case DIFFSTATE_THEIRSADDED
:
2241 case DIFFSTATE_YOURSREMOVED
:
2242 case DIFFSTATE_YOURSADDED
:
2243 case DIFFSTATE_EDITED
:
2244 sCopyData
+= m_pViewData
->GetLine(i
);
2245 sCopyData
+= _T("\r\n");
2249 // remove the last \r\n
2250 sCopyData
= sCopyData
.Left(sCopyData
.GetLength()-2);
2251 // remove the non-selected chars from the first line
2252 sCopyData
= sCopyData
.Mid(m_ptSelectionStartPos
.x
);
2253 // remove the non-selected chars from the last line
2254 int lastLinePos
= sCopyData
.ReverseFind('\n');
2256 if (lastLinePos
== 0)
2257 lastLinePos
-= m_ptSelectionStartPos
.x
;
2258 sCopyData
= sCopyData
.Left(lastLinePos
+m_ptSelectionEndPos
.x
);
2259 if (!sCopyData
.IsEmpty())
2261 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
2265 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
2267 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0) {
2268 --m_pMainFrame
->m_nMoveMovesToIgnore
;
2269 CView::OnMouseMove(nFlags
, point
);
2273 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2274 nMouseLine
--; //we need the index
2275 if (nMouseLine
< -1)
2279 ShowDiffLines(nMouseLine
);
2281 KillTimer(IDT_SCROLLTIMER
);
2282 if (nFlags
& MK_LBUTTON
)
2284 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2285 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2286 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2287 if (((m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))&&
2288 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
2290 m_ptCaretPos
.y
= nMouseLine
;
2291 m_ptCaretPos
.x
= charIndex
;
2298 if (nMouseLine
< m_nTopLine
)
2300 ScrollToLine(m_nTopLine
-1, TRUE
);
2301 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2303 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2305 ScrollToLine(m_nTopLine
+1, TRUE
);
2306 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2308 if (charIndex
<= m_nOffsetChar
)
2311 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2313 if (charIndex
>= (GetScreenChars()+m_nOffsetChar
))
2316 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2320 if (!m_bMouseWithin
)
2322 m_bMouseWithin
= TRUE
;
2323 TRACKMOUSEEVENT tme
;
2324 tme
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2325 tme
.dwFlags
= TME_LEAVE
;
2326 tme
.hwndTrack
= m_hWnd
;
2327 _TrackMouseEvent(&tme
);
2330 CView::OnMouseMove(nFlags
, point
);
2333 void CBaseView::OnMouseLeave()
2336 m_bMouseWithin
= FALSE
;
2337 KillTimer(IDT_SCROLLTIMER
);
2338 CView::OnMouseLeave();
2341 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
2343 if (nIDEvent
== IDT_SCROLLTIMER
)
2346 GetCursorPos(&point
);
2347 ScreenToClient(&point
);
2348 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2349 nMouseLine
--; //we need the index
2350 if (nMouseLine
< -1)
2354 if (GetKeyState(VK_LBUTTON
)&0x8000)
2356 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2357 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2358 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2359 if (nMouseLine
< m_nTopLine
)
2361 ScrollToLine(m_nTopLine
-1, TRUE
);
2362 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2364 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2366 ScrollToLine(m_nTopLine
+1, TRUE
);
2367 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2369 if (charIndex
<= m_nOffsetChar
)
2372 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2374 if (charIndex
>= GetScreenChars())
2377 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2383 CView::OnTimer(nIDEvent
);
2386 void CBaseView::SelectLines(int nLine1
, int nLine2
)
2390 m_nSelBlockStart
= nLine1
;
2391 m_nSelBlockEnd
= nLine2
;
2395 void CBaseView::ShowDiffLines(int nLine
)
2397 if ((nLine
>= m_nTopLine
)&&(nLine
< GetLineCount()))
2399 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
)&&(m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
)&&(!m_pMainFrame
->m_bOneWay
))
2401 nLine
= (nLine
> m_pwndRight
->m_pViewData
->GetCount() ? -1 : nLine
);
2402 nLine
= (nLine
> m_pwndLeft
->m_pViewData
->GetCount() ? -1 : nLine
);
2406 if (nLine
!= m_nMouseLine
)
2408 m_nMouseLine
= nLine
;
2409 if (nLine
>= GetLineCount())
2411 m_pwndLineDiffBar
->ShowLines(nLine
);
2418 m_pwndLineDiffBar
->ShowLines(nLine
);
2422 void CBaseView::UseTheirAndYourBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2424 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2426 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2428 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2429 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndLeft
->m_pViewData
->GetLine(i
));
2430 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2431 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndLeft
->m_pViewData
->GetState(i
));
2432 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, m_pwndBottom
->lineendings
);
2433 if (m_pwndBottom
->IsLineConflicted(i
))
2435 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2436 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2438 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2440 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2443 // your block is done, now insert their block
2444 int index
= m_nSelBlockEnd
+1;
2445 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2447 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2448 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndRight
->m_pViewData
->GetData(i
));
2449 if (m_pwndBottom
->IsLineConflicted(index
))
2451 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2452 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2454 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2456 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2459 // adjust line numbers
2460 for (int i
=m_nSelBlockEnd
+1; i
<GetLineCount(); ++i
)
2462 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2464 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2467 // now insert an empty block in both yours and theirs
2468 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2470 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2471 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2472 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2473 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2475 RecalcAllVertScrollBars();
2476 m_pwndBottom
->SetModified();
2477 m_pwndLeft
->SetModified();
2478 m_pwndRight
->SetModified();
2481 void CBaseView::UseYourAndTheirBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2483 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2485 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2487 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2488 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndRight
->m_pViewData
->GetLine(i
));
2489 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2490 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndRight
->m_pViewData
->GetState(i
));
2491 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2492 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, m_pwndBottom
->lineendings
);
2493 if (m_pwndBottom
->IsLineConflicted(i
))
2495 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2496 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2498 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2500 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2503 // your block is done, now insert their block
2504 int index
= m_nSelBlockEnd
+1;
2505 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2507 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2508 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2509 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2510 if (m_pwndBottom
->IsLineConflicted(index
))
2512 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2513 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2515 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2517 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2520 // adjust line numbers
2521 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndBottom
->GetLineCount(); ++i
)
2523 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2525 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2528 // now insert an empty block in both yours and theirs
2529 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2531 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2532 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2533 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2534 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2537 RecalcAllVertScrollBars();
2538 m_pwndBottom
->SetModified();
2539 m_pwndLeft
->SetModified();
2540 m_pwndRight
->SetModified();
2543 void CBaseView::UseBothRightFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2545 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2547 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2549 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2550 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2553 // your block is done, now insert their block
2554 int index
= m_nSelBlockEnd
+1;
2555 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2557 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2558 m_pwndRight
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2559 m_pwndRight
->m_pViewData
->SetState(index
++, DIFFSTATE_THEIRSADDED
);
2561 // adjust line numbers
2563 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2565 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2567 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2570 // now insert an empty block in the left view
2571 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2573 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2574 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2576 RecalcAllVertScrollBars();
2577 m_pwndLeft
->SetModified();
2578 m_pwndRight
->SetModified();
2581 void CBaseView::UseBothLeftFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2583 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2585 // get line number from just before the block
2586 long linenumber
= 0;
2587 if (m_nSelBlockStart
> 0)
2588 linenumber
= m_pwndRight
->m_pViewData
->GetLineNumber(m_nSelBlockStart
-1);
2590 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2592 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2593 m_pwndRight
->m_pViewData
->InsertData(i
, m_pwndLeft
->m_pViewData
->GetLine(i
), DIFFSTATE_THEIRSADDED
, linenumber
++, m_pwndLeft
->m_pViewData
->GetLineEnding(i
));
2595 // adjust line numbers
2596 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2598 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2600 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(m_nSelBlockEnd
-m_nSelBlockStart
)+1);
2603 // now insert an empty block in left view
2604 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2606 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+ 1);
2607 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+ 1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2609 RecalcAllVertScrollBars();
2610 m_pwndLeft
->SetModified();
2611 m_pwndRight
->SetModified();
2614 void CBaseView::UpdateCaret()
2616 if (m_ptCaretPos
.y
>= GetLineCount())
2617 m_ptCaretPos
.y
= GetLineCount()-1;
2618 if (m_ptCaretPos
.y
< 0)
2620 if (m_ptCaretPos
.x
> GetLineLength(m_ptCaretPos
.y
))
2621 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2622 if (m_ptCaretPos
.x
< 0)
2625 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2627 if (m_bFocused
&& !m_bCaretHidden
&&
2628 m_ptCaretPos
.y
>= m_nTopLine
&&
2629 m_ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
2630 nCaretOffset
>= m_nOffsetChar
&&
2631 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
2633 CreateSolidCaret(2, GetLineHeight());
2634 SetCaretPos(TextToClient(m_ptCaretPos
));
2643 void CBaseView::EnsureCaretVisible()
2645 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2647 if (m_ptCaretPos
.y
< m_nTopLine
)
2648 ScrollAllToLine(m_ptCaretPos
.y
);
2649 if (m_ptCaretPos
.y
>= (m_nTopLine
+GetScreenLines()))
2650 ScrollAllToLine(m_ptCaretPos
.y
-GetScreenLines()+1);
2651 if (nCaretOffset
< m_nOffsetChar
)
2652 ScrollToChar(nCaretOffset
);
2653 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
2654 ScrollToChar(nCaretOffset
-GetScreenChars()+1);
2657 int CBaseView::CalculateActualOffset(int nLineIndex
, int nCharIndex
) const
2659 int nLength
= GetLineLength(nLineIndex
);
2660 ASSERT(nCharIndex
>= 0);
2661 if (nCharIndex
> nLength
)
2662 nCharIndex
= nLength
;
2663 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
2665 int nTabSize
= GetTabSize();
2666 for (int I
= 0; I
< nCharIndex
; I
++)
2668 if (pszChars
[I
] == _T('\t'))
2669 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2676 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
) const
2678 int nLength
= GetLineLength(nLineIndex
);
2679 LPCTSTR pszLine
= GetLineChars(nLineIndex
);
2682 int nTabSize
= GetTabSize();
2683 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
2685 if (pszLine
[nIndex
] == _T('\t'))
2686 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2694 POINT
CBaseView::TextToClient(const POINT
& point
)
2697 pt
.y
= max(0, (point
.y
- m_nTopLine
) * GetLineHeight());
2698 pt
.x
= CalculateActualOffset(point
.y
, point
.x
);
2700 pt
.x
= (pt
.x
- m_nOffsetChar
) * GetCharWidth() + GetMarginWidth();
2701 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
2705 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2707 CView::OnChar(nChar
, nRepCnt
, nFlags
);
2712 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
2713 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
2716 if ((nChar
> 31)||(nChar
== VK_TAB
))
2718 RemoveSelectedText();
2719 AddUndoLine(m_ptCaretPos
.y
);
2720 CString sLine
= GetLineChars(m_ptCaretPos
.y
);
2721 sLine
.Insert(m_ptCaretPos
.x
, (wchar_t)nChar
);
2722 m_pViewData
->SetLine(m_ptCaretPos
.y
, sLine
);
2723 m_pViewData
->SetState(m_ptCaretPos
.y
, DIFFSTATE_EDITED
);
2724 if (m_pViewData
->GetLineEnding(m_ptCaretPos
.y
) == EOL_NOENDING
&& m_pViewData
->GetCount() - 1 != m_ptCaretPos
.y
)
2725 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, lineendings
);
2729 else if (nChar
== 10)
2731 AddUndoLine(m_ptCaretPos
.y
);
2732 EOL eol
= m_pViewData
->GetLineEnding(m_ptCaretPos
.y
);
2733 EOL newEOL
= EOL_CRLF
;
2742 newEOL
= (m_pViewData
->GetCount() - 1 == m_ptCaretPos
.y
&& !m_pViewData
->GetLine(m_ptCaretPos
.y
).IsEmpty()) ? EOL_NOENDING
: EOL_CRLF
;
2745 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, newEOL
);
2746 m_pViewData
->SetState(m_ptCaretPos
.y
, DIFFSTATE_EDITED
);
2749 else if (nChar
== VK_RETURN
)
2751 // insert a new, fresh and empty line below the cursor
2752 RemoveSelectedText();
2753 AddUndoLine(m_ptCaretPos
.y
, true);
2754 EOL eOriginalEnding
= m_pViewData
->GetLineEnding(m_ptCaretPos
.y
);
2755 if (m_pViewData
->GetCount() - 2 == m_ptCaretPos
.y
&& eOriginalEnding
== EOL_NOENDING
)
2756 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, lineendings
);
2757 // move the cursor to the new line
2760 if (m_pViewData
->GetCount() - 1 == m_ptCaretPos
.y
)
2761 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, eOriginalEnding
);
2765 return; // Unknown control character -- ignore it.
2767 EnsureCaretVisible();
2773 void CBaseView::AddUndoLine(int nLine
, bool bAddEmptyLine
)
2775 viewstate leftstate
;
2776 viewstate rightstate
;
2777 viewstate bottomstate
;
2778 leftstate
.AddLineFormView(m_pwndLeft
, nLine
, bAddEmptyLine
);
2779 rightstate
.AddLineFormView(m_pwndRight
, nLine
, bAddEmptyLine
);
2780 bottomstate
.AddLineFormView(m_pwndBottom
, nLine
, bAddEmptyLine
);
2781 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2784 void CBaseView::AddEmptyLine(int nLineIndex
)
2786 if (m_pViewData
== NULL
)
2788 if (!m_bCaretHidden
)
2790 CString sPartLine
= GetLineChars(nLineIndex
);
2791 m_pViewData
->SetLine(nLineIndex
, sPartLine
.Left(m_ptCaretPos
.x
));
2792 sPartLine
= sPartLine
.Mid(m_ptCaretPos
.x
);
2793 m_pViewData
->InsertData(nLineIndex
+1, sPartLine
, DIFFSTATE_EDITED
, -1, lineendings
);
2796 m_pViewData
->InsertData(nLineIndex
+1, _T(""), DIFFSTATE_EDITED
, -1, lineendings
);
2800 void CBaseView::RemoveLine(int nLineIndex
)
2802 if (m_pViewData
== NULL
)
2804 m_pViewData
->RemoveData(nLineIndex
);
2805 if (m_ptCaretPos
.y
>= GetLineCount())
2806 m_ptCaretPos
.y
= GetLineCount()-1;
2810 void CBaseView::RemoveSelectedText()
2812 if (m_pViewData
== NULL
)
2814 if (!HasTextSelection())
2817 viewstate rightstate
;
2818 viewstate bottomstate
;
2819 viewstate leftstate
;
2820 std::vector
<LONG
> linestoremove
;
2821 for (LONG i
= m_ptSelectionStartPos
.y
; i
<= m_ptSelectionEndPos
.y
; ++i
)
2823 if (i
== m_ptSelectionStartPos
.y
)
2825 CString sLine
= GetLineChars(m_ptSelectionStartPos
.y
);
2827 if (i
== m_ptSelectionStartPos
.y
)
2829 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2831 leftstate
.difflines
[i
] = m_pwndLeft
->m_pViewData
->GetLine(i
);
2832 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2834 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2836 rightstate
.difflines
[i
] = m_pwndRight
->m_pViewData
->GetLine(i
);
2837 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2839 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2841 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2842 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2844 newLine
= sLine
.Left(m_ptSelectionStartPos
.x
);
2845 sLine
= GetLineChars(m_ptSelectionEndPos
.y
);
2846 newLine
= newLine
+ sLine
.Mid(m_ptSelectionEndPos
.x
);
2848 m_pViewData
->SetLine(i
, newLine
);
2849 m_pViewData
->SetState(i
, DIFFSTATE_EDITED
);
2854 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2856 leftstate
.removedlines
[i
] = m_pwndLeft
->m_pViewData
->GetData(i
);
2858 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2860 rightstate
.removedlines
[i
] = m_pwndRight
->m_pViewData
->GetData(i
);
2862 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2864 bottomstate
.removedlines
[i
] = m_pwndBottom
->m_pViewData
->GetData(i
);
2866 linestoremove
.push_back(i
);
2869 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2870 // remove the lines at the end, to avoid problems with line indexes
2871 if (linestoremove
.size())
2873 std::vector
<LONG
>::const_iterator it
= linestoremove
.begin();
2874 int nLineToRemove
= *it
;
2875 for ( ; it
!= linestoremove
.end(); ++it
)
2878 m_pwndLeft
->RemoveLine(nLineToRemove
);
2880 m_pwndRight
->RemoveLine(nLineToRemove
);
2882 m_pwndBottom
->RemoveLine(nLineToRemove
);
2886 m_ptCaretPos
= m_ptSelectionStartPos
;
2890 EnsureCaretVisible();
2894 void CBaseView::PasteText()
2896 if (!OpenClipboard())
2899 CString sClipboardText
;
2900 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
2903 LPCSTR lpstr
= (LPCSTR
)GlobalLock(hglb
);
2904 sClipboardText
= CString(lpstr
);
2907 hglb
= GetClipboardData(CF_UNICODETEXT
);
2910 LPCTSTR lpstr
= (LPCTSTR
)GlobalLock(hglb
);
2911 sClipboardText
= lpstr
;
2916 if (sClipboardText
.IsEmpty())
2919 sClipboardText
.Replace(_T("\r\n"), _T("\r"));
2920 sClipboardText
.Replace('\n', '\r');
2921 // We want to undo the insertion in a single step.
2922 CUndo::GetInstance().BeginGrouping();
2923 // use the easy way to insert text:
2924 // insert char by char, using the OnChar() method
2925 for (int i
=0; i
<sClipboardText
.GetLength(); ++i
)
2927 OnChar(sClipboardText
[i
], 0, 0);
2929 CUndo::GetInstance().EndGrouping();
2932 void CBaseView::OnCaretDown()
2935 m_ptCaretPos
.y
= min(m_ptCaretPos
.y
, GetLineCount()-1);
2936 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2937 if (GetKeyState(VK_SHIFT
)&0x8000)
2942 EnsureCaretVisible();
2943 ShowDiffLines(m_ptCaretPos
.y
);
2946 bool CBaseView::MoveCaretLeft()
2948 if (m_ptCaretPos
.x
== 0)
2950 if (m_ptCaretPos
.y
> 0)
2953 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2965 bool CBaseView::MoveCaretRight()
2967 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
2969 if (m_ptCaretPos
.y
< (GetLineCount() - 1))
2984 void CBaseView::UpdateGoalPos()
2986 m_nCaretGoalPos
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2989 void CBaseView::OnCaretLeft()
2992 if (GetKeyState(VK_SHIFT
)&0x8000)
2996 EnsureCaretVisible();
3000 void CBaseView::OnCaretRight()
3003 if (GetKeyState(VK_SHIFT
)&0x8000)
3007 EnsureCaretVisible();
3011 void CBaseView::OnCaretUp()
3014 m_ptCaretPos
.y
= max(0, m_ptCaretPos
.y
);
3015 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
3016 if (GetKeyState(VK_SHIFT
)&0x8000)
3021 EnsureCaretVisible();
3022 ShowDiffLines(m_ptCaretPos
.y
);
3025 bool CBaseView::IsWordSeparator(wchar_t ch
) const
3027 return ch
== ' ' || ch
== '\t' || (m_sWordSeparators
.Find(ch
) >= 0);
3030 bool CBaseView::IsCaretAtWordBoundary() const
3032 LPCTSTR line
= GetLineChars(m_ptCaretPos
.y
);
3034 return false; // no boundary at the empty lines
3035 if (m_ptCaretPos
.x
== 0)
3036 return !IsWordSeparator(line
[m_ptCaretPos
.x
]);
3037 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
3038 return !IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3040 IsWordSeparator(line
[m_ptCaretPos
.x
]) !=
3041 IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3044 void CBaseView::OnCaretWordleft()
3046 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3049 if (GetKeyState(VK_SHIFT
)&0x8000)
3053 EnsureCaretVisible();
3057 void CBaseView::OnCaretWordright()
3059 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3062 if (GetKeyState(VK_SHIFT
)&0x8000)
3066 EnsureCaretVisible();
3070 void CBaseView::ClearCurrentSelection()
3072 m_ptSelectionStartPos
= m_ptCaretPos
;
3073 m_ptSelectionEndPos
= m_ptCaretPos
;
3074 m_ptSelectionOrigin
= m_ptCaretPos
;
3075 m_nSelBlockStart
= -1;
3076 m_nSelBlockEnd
= -1;
3080 void CBaseView::ClearSelection()
3083 m_pwndLeft
->ClearCurrentSelection();
3085 m_pwndRight
->ClearCurrentSelection();
3087 m_pwndBottom
->ClearCurrentSelection();
3090 void CBaseView::AdjustSelection()
3092 if ((m_ptCaretPos
.y
< m_ptSelectionOrigin
.y
) ||
3093 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
<= m_ptSelectionOrigin
.x
))
3095 m_ptSelectionStartPos
= m_ptCaretPos
;
3096 m_ptSelectionEndPos
= m_ptSelectionOrigin
;
3099 if ((m_ptCaretPos
.y
> m_ptSelectionOrigin
.y
) ||
3100 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
>= m_ptSelectionOrigin
.x
))
3102 m_ptSelectionStartPos
= m_ptSelectionOrigin
;
3103 m_ptSelectionEndPos
= m_ptCaretPos
;
3106 SetupSelection(min(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
), max(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
));
3111 void CBaseView::OnEditCut()
3113 if (!m_bCaretHidden
)
3116 RemoveSelectedText();
3120 void CBaseView::OnEditPaste()
3122 if (!m_bCaretHidden
)
3128 void CBaseView::OnEditSelectall()
3130 int nCount
= GetLineCount();
3131 SetupSelection(0, nCount
);
3132 m_ptSelectionStartPos
.x
= 0;
3133 m_ptSelectionStartPos
.y
= 0;
3135 m_ptSelectionEndPos
.y
= nCount
-1;
3136 CString sLine
= GetLineChars(nCount
-1);
3137 m_ptSelectionEndPos
.x
= sLine
.GetLength();