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::UNICODE_BE
:
286 sBarText
= _T("UTF-16BE ");
288 case CFileTextLines::UTF8
:
289 sBarText
= _T("UTF8 ");
291 case CFileTextLines::UTF8BOM
:
292 sBarText
= _T("UTF8 BOM ");
299 sBarText
+= _T("LF ");
302 sBarText
+= _T("CRLF ");
305 sBarText
+= _T("LFCR ");
308 sBarText
+= _T("CR ");
312 if (sBarText
.IsEmpty())
313 sBarText
+= _T(" / ");
317 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
318 if (!sBarText
.IsEmpty())
319 sBarText
+= _T(" / ");
324 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
325 if (!sBarText
.IsEmpty())
326 sBarText
+= _T(" / ");
329 if (nConflictedLines
)
331 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
332 if (!sBarText
.IsEmpty())
333 sBarText
+= _T(" / ");
341 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
342 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
344 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
346 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
348 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
349 sBarText
= sTemp
+sBarText
;
351 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
353 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
354 sBarText
= sTemp
+sBarText
;
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
);
369 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
371 if (!CView::PreCreateWindow(cs
))
374 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
375 cs
.style
&= ~WS_BORDER
;
376 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
377 ::LoadCursor(NULL
, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1), NULL
);
379 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
380 if (pParentWnd
== NULL
|| ! pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
382 // View must always create its own scrollbars,
383 // if only it's not used within splitter
384 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
386 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
390 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/, BOOL bStrikeOut
/*= FALSE*/)
399 if (m_apFonts
[nIndex
] == NULL
)
401 m_apFonts
[nIndex
] = new CFont
;
402 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
403 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
404 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
405 m_lfBaseFont
.lfStrikeOut
= (BYTE
) bStrikeOut
;
407 m_lfBaseFont
.lfStrikeOut
= (BYTE
)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE
);
411 m_lfBaseFont
.lfHeight
= -MulDiv((DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC
->m_hDC
, LOGPIXELSY
), 72);
414 _tcsncpy_s(m_lfBaseFont
.lfFaceName
, 32, (LPCTSTR
)(CString
)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
415 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
417 delete m_apFonts
[nIndex
];
418 m_apFonts
[nIndex
] = NULL
;
419 return CView::GetFont();
422 return m_apFonts
[nIndex
];
425 void CBaseView::CalcLineCharDim()
428 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
429 CSize szCharExt
= pDC
->GetTextExtent(_T("X"));
430 m_nLineHeight
= szCharExt
.cy
;
431 if (m_nLineHeight
<= 0)
433 m_nCharWidth
= szCharExt
.cx
;
434 if (m_nCharWidth
<= 0)
436 pDC
->SelectObject(pOldFont
);
440 int CBaseView::GetScreenChars()
442 if (m_nScreenChars
== -1)
445 GetClientRect(&rect
);
446 m_nScreenChars
= (rect
.Width() - GetMarginWidth()) / GetCharWidth();
448 return m_nScreenChars
;
451 int CBaseView::GetAllMinScreenChars() const
454 if (IsLeftViewGood())
455 nChars
= m_pwndLeft
->GetScreenChars();
456 if (IsRightViewGood())
457 nChars
= (nChars
< m_pwndRight
->GetScreenChars() ? nChars
: m_pwndRight
->GetScreenChars());
458 if (IsBottomViewGood())
459 nChars
= (nChars
< m_pwndBottom
->GetScreenChars() ? nChars
: m_pwndBottom
->GetScreenChars());
463 int CBaseView::GetAllMaxLineLength() const
466 if (IsLeftViewGood())
467 nLength
= m_pwndLeft
->GetMaxLineLength();
468 if (IsRightViewGood())
469 nLength
= (nLength
> m_pwndRight
->GetMaxLineLength() ? nLength
: m_pwndRight
->GetMaxLineLength());
470 if (IsBottomViewGood())
471 nLength
= (nLength
> m_pwndBottom
->GetMaxLineLength() ? nLength
: m_pwndBottom
->GetMaxLineLength());
475 int CBaseView::GetLineHeight()
477 if (m_nLineHeight
== -1)
479 if (m_nLineHeight
<= 0)
481 return m_nLineHeight
;
484 int CBaseView::GetCharWidth()
486 if (m_nCharWidth
== -1)
488 if (m_nCharWidth
<= 0)
493 int CBaseView::GetMaxLineLength()
495 if (m_nMaxLineLength
== -1)
497 m_nMaxLineLength
= 0;
498 int nLineCount
= GetLineCount();
499 for (int i
=0; i
<nLineCount
; i
++)
501 int nActualLength
= GetLineActualLength(i
);
502 if (m_nMaxLineLength
< nActualLength
)
503 m_nMaxLineLength
= nActualLength
;
506 return m_nMaxLineLength
;
509 int CBaseView::GetLineActualLength(int index
) const
511 if (m_pViewData
== NULL
)
514 return CalculateActualOffset(index
, GetLineLength(index
));
517 int CBaseView::GetLineLength(int index
) const
519 if (m_pViewData
== NULL
)
521 if (m_pViewData
->GetCount() == 0)
523 int nLineLength
= m_pViewData
->GetLine(index
).GetLength();
524 ASSERT(nLineLength
>= 0);
528 int CBaseView::GetLineCount() const
530 if (m_pViewData
== NULL
)
532 int nLineCount
= m_pViewData
->GetCount();
533 ASSERT(nLineCount
>= 0);
537 LPCTSTR
CBaseView::GetLineChars(int index
) const
539 if (m_pViewData
== NULL
)
541 if (m_pViewData
->GetCount() == 0)
543 return m_pViewData
->GetLine(index
);
546 void CBaseView::CheckOtherView()
548 if (m_bOtherDiffChecked
)
550 // find out what the 'other' file is
551 m_pOtherViewData
= NULL
;
552 if (this == m_pwndLeft
&& IsRightViewGood())
553 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
555 if (this == m_pwndRight
&& IsLeftViewGood())
556 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
558 m_bOtherDiffChecked
= true;
561 CString
CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
)
563 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
566 DiffStates origstate
= viewData
->GetState(nLineIndex
);
568 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
569 int nStartBlock
= nLineIndex
;
570 int nEndBlock
= nLineIndex
;
571 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
573 DiffStates state
= viewData
->GetState(nStartBlock
- 1);
574 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
576 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
581 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
583 DiffStates state
= viewData
->GetState(nEndBlock
+ 1);
584 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
586 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
593 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
594 block
+= viewData
->GetLine(i
);
598 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
)
600 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
602 if (!m_pOtherViewData
)
605 (m_pViewData
->GetState(nLineIndex
) == DIFFSTATE_NORMAL
) &&
606 (m_pOtherViewData
->GetLine(nLineIndex
) == m_pViewData
->GetLine(nLineIndex
))
610 CString mine
= GetWhitespaceBlock(m_pViewData
, nLineIndex
);
611 CString other
= GetWhitespaceBlock(m_pOtherViewData
, min(nLineIndex
, m_pOtherViewData
->GetCount() - 1));
612 bIdentical
= mine
== other
;
623 return (mine
== other
) && (!mine
.IsEmpty());
626 int CBaseView::GetLineNumber(int index
) const
628 if (m_pViewData
== NULL
)
630 if (m_pViewData
->GetLineNumber(index
)==DIFF_EMPTYLINENUMBER
)
632 return m_pViewData
->GetLineNumber(index
);
635 int CBaseView::GetScreenLines()
637 if (m_nScreenLines
== -1)
640 sbi
.cbSize
= sizeof(sbi
);
641 GetScrollBarInfo(OBJID_HSCROLL
, &sbi
);
642 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
645 GetClientRect(&rect
);
646 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
648 return m_nScreenLines
;
651 int CBaseView::GetAllMinScreenLines() const
654 if (IsLeftViewGood())
655 nLines
= m_pwndLeft
->GetScreenLines();
656 if (IsRightViewGood())
657 nLines
= (nLines
< m_pwndRight
->GetScreenLines() ? nLines
: m_pwndRight
->GetScreenLines());
658 if (IsBottomViewGood())
659 nLines
= (nLines
< m_pwndBottom
->GetScreenLines() ? nLines
: m_pwndBottom
->GetScreenLines());
663 int CBaseView::GetAllLineCount() const
666 if (IsLeftViewGood())
667 nLines
= m_pwndLeft
->GetLineCount();
668 if (IsRightViewGood())
669 nLines
= (nLines
> m_pwndRight
->GetLineCount() ? nLines
: m_pwndRight
->GetLineCount());
670 if (IsBottomViewGood())
671 nLines
= (nLines
> m_pwndBottom
->GetLineCount() ? nLines
: m_pwndBottom
->GetLineCount());
675 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
677 if (IsLeftViewGood())
678 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
679 if (IsRightViewGood())
680 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
681 if (IsBottomViewGood())
682 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
685 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
688 si
.cbSize
= sizeof(si
);
692 si
.nPos
= m_nTopLine
;
696 EnableScrollBarCtrl(SB_VERT
, TRUE
);
697 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
702 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
704 si
.nMax
= GetAllLineCount();
705 si
.nPage
= GetAllMinScreenLines();
706 si
.nPos
= m_nTopLine
;
708 VERIFY(SetScrollInfo(SB_VERT
, &si
));
711 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
713 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
715 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
717 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
719 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
721 m_pwndLocator
->Invalidate();
724 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
726 // Note we cannot use nPos because of its 16-bit nature
728 si
.cbSize
= sizeof(si
);
730 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
732 int nPageLines
= GetScreenLines();
733 int nLineCount
= GetLineCount();
739 static LONG textwidth
= 0;
740 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
747 nNewTopLine
= nLineCount
- nPageLines
+ 1;
750 nNewTopLine
= m_nTopLine
- 1;
753 nNewTopLine
= m_nTopLine
+ 1;
756 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
759 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
761 case SB_THUMBPOSITION
:
762 m_ScrollTool
.Clear();
763 nNewTopLine
= si
.nTrackPos
;
767 nNewTopLine
= si
.nTrackPos
;
768 if (GetFocus() == this)
770 GetClientRect(&thumbrect
);
771 ClientToScreen(&thumbrect
);
772 thumbpoint
.x
= thumbrect
.right
;
773 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
774 m_ScrollTool
.Init(&thumbpoint
);
777 CString sTemp
= sFormat
;
778 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
779 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
781 thumbpoint
.x
-= textwidth
;
782 int line
= GetLineNumber(nNewTopLine
);
784 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
786 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
795 if (nNewTopLine
>= nLineCount
)
796 nNewTopLine
= nLineCount
- 1;
797 ScrollToLine(nNewTopLine
);
800 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
802 if (IsLeftViewGood())
803 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
804 if (IsRightViewGood())
805 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
806 if (IsBottomViewGood())
807 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
810 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
813 si
.cbSize
= sizeof(si
);
817 si
.nPos
= m_nOffsetChar
;
821 EnableScrollBarCtrl(SB_HORZ
, TRUE
);
822 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar
> 0)
827 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
829 si
.nMax
= GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
830 si
.nPage
= GetAllMinScreenChars();
831 si
.nPos
= m_nOffsetChar
;
833 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
836 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
838 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
840 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
842 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
844 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
846 m_pwndLocator
->Invalidate();
849 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
852 si
.cbSize
= sizeof(si
);
854 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
856 int nPageChars
= GetScreenChars();
857 int nMaxLineLength
= GetMaxLineLength();
866 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
869 nNewOffset
= m_nOffsetChar
- 1;
872 nNewOffset
= m_nOffsetChar
+ 1;
875 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
878 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
880 case SB_THUMBPOSITION
:
882 nNewOffset
= si
.nTrackPos
;
888 if (nNewOffset
>= nMaxLineLength
)
889 nNewOffset
= nMaxLineLength
- 1;
892 ScrollToChar(nNewOffset
, TRUE
);
895 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
897 if (m_nOffsetChar
!= nNewOffsetChar
)
899 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
900 m_nOffsetChar
= nNewOffsetChar
;
902 GetClientRect(&rcScroll
);
903 rcScroll
.left
+= GetMarginWidth();
904 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
905 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
906 // update the view header
909 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
910 InvalidateRect(&rcScroll
, FALSE
);
913 RecalcHorzScrollBar(TRUE
);
918 void CBaseView::ScrollSide(int delta
)
920 int nNewOffset
= m_nOffsetChar
;
922 int nMaxLineLength
= GetMaxLineLength();
923 if (nNewOffset
>= nMaxLineLength
)
924 nNewOffset
= nMaxLineLength
- 1;
927 ScrollToChar(nNewOffset
, TRUE
);
928 if (m_pwndLineDiffBar
)
929 m_pwndLineDiffBar
->Invalidate();
933 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
935 if (m_nTopLine
!= nNewTopLine
)
939 int nScrollLines
= m_nTopLine
- nNewTopLine
;
940 m_nTopLine
= nNewTopLine
;
942 GetClientRect(&rcScroll
);
943 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
944 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
947 RecalcVertScrollBar(TRUE
);
953 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
955 pdc
->FillSolidRect(rect
, ::GetSysColor(COLOR_SCROLLBAR
));
957 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
959 DiffStates state
= m_pViewData
->GetState(nLineIndex
);
963 case DIFFSTATE_ADDED
:
964 case DIFFSTATE_THEIRSADDED
:
965 case DIFFSTATE_YOURSADDED
:
966 case DIFFSTATE_IDENTICALADDED
:
967 case DIFFSTATE_CONFLICTADDED
:
970 case DIFFSTATE_REMOVED
:
971 case DIFFSTATE_THEIRSREMOVED
:
972 case DIFFSTATE_YOURSREMOVED
:
973 case DIFFSTATE_IDENTICALREMOVED
:
974 icon
= m_hRemovedIcon
;
976 case DIFFSTATE_CONFLICTED
:
977 icon
= m_hConflictedIcon
;
979 case DIFFSTATE_CONFLICTED_IGNORED
:
980 icon
= m_hConflictedIgnoredIcon
;
982 case DIFFSTATE_EDITED
:
983 icon
= m_hEditedIcon
;
988 bool bIdentical
= false;
989 if ((state
!= DIFFSTATE_EDITED
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
)))
994 icon
= m_hWhitespaceBlockIcon
;
999 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ 2, rect
.top
+ (rect
.Height()-16)/2, icon
, 16, 16, NULL
, NULL
, DI_NORMAL
);
1001 if ((m_bViewLinenumbers
)&&(m_nDigits
))
1003 int nLineNumber
= GetLineNumber(nLineIndex
);
1004 if (nLineNumber
>= 0)
1006 CString sLinenumberFormat
;
1007 CString sLinenumber
;
1008 sLinenumberFormat
.Format(_T("%%%dd"), m_nDigits
);
1009 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1010 pdc
->SetBkColor(::GetSysColor(COLOR_SCROLLBAR
));
1011 pdc
->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT
));
1013 pdc
->SelectObject(GetFont());
1014 pdc
->ExtTextOut(rect
.left
+ 18, rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, NULL
);
1020 int CBaseView::GetMarginWidth()
1022 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1024 int nWidth
= GetCharWidth();
1027 int nLength
= (int)m_pViewData
->GetCount();
1028 // find out how many digits are needed to show the highest line number
1030 while (nLength
/ 10)
1035 m_nDigits
= nDigits
;
1037 return (MARGINWIDTH
+ (m_nDigits
* nWidth
) + 2);
1042 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1044 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1045 COLORREF crBk
, crFg
;
1046 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL
, crBk
, crFg
);
1047 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
1048 if (IsBottomViewGood())
1050 pdc
->SetBkColor(crBk
);
1055 if (this == m_pwndRight
)
1057 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED
, crBk
, crFg
);
1058 pdc
->SetBkColor(crBk
);
1062 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED
, crBk
, crFg
);
1063 pdc
->SetBkColor(crBk
);
1066 pdc
->FillSolidRect(textrect
, crBk
);
1068 pdc
->SetTextColor(crFg
);
1070 pdc
->SelectObject(GetFont(FALSE
, TRUE
, FALSE
));
1073 if (m_sWindowName
.Left(2).Compare(_T("* "))!=0)
1074 m_sWindowName
= _T("* ") + m_sWindowName
;
1078 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
1079 m_sWindowName
= m_sWindowName
.Mid(2);
1081 CString sViewTitle
= m_sWindowName
;
1082 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1083 if (nStringLength
> rect
.Width())
1085 int offset
= min(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1087 sViewTitle
= m_sWindowName
.Mid(offset
);
1089 pdc
->ExtTextOut(max(rect
.left
+ (rect
.Width()-nStringLength
)/2, 1),
1090 rect
.top
+(HEADERHEIGHT
/2), ETO_CLIPPED
, textrect
, sViewTitle
, NULL
);
1091 if (this->GetFocus() == this)
1092 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1094 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1097 void CBaseView::OnDraw(CDC
* pDC
)
1100 GetClientRect(rcClient
);
1102 int nLineCount
= GetLineCount();
1103 int nLineHeight
= GetLineHeight();
1106 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1107 if (m_pCacheBitmap
== NULL
)
1109 m_pCacheBitmap
= new CBitmap
;
1110 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1112 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1114 DrawHeader(pDC
, rcClient
);
1118 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1119 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1120 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1121 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1123 int nCurrentLine
= m_nTopLine
;
1124 while (rcLine
.top
< rcClient
.bottom
)
1126 if (nCurrentLine
< nLineCount
)
1128 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1129 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1133 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1134 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1137 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1140 rcLine
.OffsetRect(0, nLineHeight
);
1143 cacheDC
.SelectObject(pOldBitmap
);
1147 BOOL
CBaseView::IsLineRemoved(int nLineIndex
)
1149 DiffStates state
= DIFFSTATE_UNKNOWN
;
1151 state
= m_pViewData
->GetState(nLineIndex
);
1155 case DIFFSTATE_REMOVED
:
1156 case DIFFSTATE_THEIRSREMOVED
:
1157 case DIFFSTATE_YOURSREMOVED
:
1158 case DIFFSTATE_IDENTICALREMOVED
:
1168 bool CBaseView::IsLineConflicted(int nLineIndex
)
1170 DiffStates state
= DIFFSTATE_UNKNOWN
;
1172 state
= m_pViewData
->GetState(nLineIndex
);
1176 case DIFFSTATE_CONFLICTED
:
1177 case DIFFSTATE_CONFLICTED_IGNORED
:
1178 case DIFFSTATE_CONFLICTEMPTY
:
1179 case DIFFSTATE_CONFLICTADDED
:
1189 COLORREF
CBaseView::IntenseColor(long scale
, COLORREF col
)
1191 // if the color is already dark (gray scale below 127),
1192 // then lighten the color by 'scale', otherwise darken it
1193 int Gray
= (((int)GetRValue(col
)) + GetGValue(col
) + GetBValue(col
))/3;
1196 long red
= MulDiv(GetRValue(col
),(255-scale
),255);
1197 long green
= MulDiv(GetGValue(col
),(255-scale
),255);
1198 long blue
= MulDiv(GetBValue(col
),(255-scale
),255);
1200 return RGB(red
, green
, blue
);
1202 long R
= MulDiv(255-GetRValue(col
),scale
,255)+GetRValue(col
);
1203 long G
= MulDiv(255-GetGValue(col
),scale
,255)+GetGValue(col
);
1204 long B
= MulDiv(255-GetBValue(col
),scale
,255)+GetBValue(col
);
1206 return RGB(R
, G
, B
);
1209 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1211 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1214 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1216 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< m_pViewData
->GetCount())))
1219 EOL ending
= m_pViewData
->GetLineEnding(nLineIndex
);
1222 HICON hEndingIcon
= NULL
;
1225 case EOL_CR
: hEndingIcon
= m_hLineEndingCR
; break;
1226 case EOL_CRLF
: hEndingIcon
= m_hLineEndingCRLF
; break;
1227 case EOL_LF
: hEndingIcon
= m_hLineEndingLF
; break;
1230 if (origin
.x
< (rc
.left
-GetCharWidth()))
1232 // If EOL style has changed, color end-of-line markers as inline differences.
1234 m_bShowInlineDiff
&& m_pOtherViewData
&&
1235 (nLineIndex
< m_pOtherViewData
->GetCount()) &&
1236 (ending
!= EOL_NOENDING
) &&
1237 (ending
!= m_pOtherViewData
->GetLineEnding(nLineIndex
) &&
1238 (m_pOtherViewData
->GetLineEnding(nLineIndex
) != EOL_NOENDING
))
1241 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1244 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), NULL
, NULL
, DI_NORMAL
);
1248 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1249 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1250 int yMiddle
= origin
.y
+ rc
.Height()/2;
1251 int xMiddle
= origin
.x
+GetCharWidth()/2;
1255 // arrow from right to left
1256 pDC
->MoveTo(origin
.x
+GetCharWidth(), yMiddle
);
1257 pDC
->LineTo(origin
.x
, yMiddle
);
1258 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1259 pDC
->MoveTo(origin
.x
, yMiddle
);
1260 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1263 // arrow from top to middle+2, then left
1264 pDC
->MoveTo(origin
.x
+GetCharWidth(), rc
.top
);
1265 pDC
->LineTo(origin
.x
+GetCharWidth(), yMiddle
);
1266 pDC
->LineTo(origin
.x
, yMiddle
);
1267 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1268 pDC
->MoveTo(origin
.x
, yMiddle
);
1269 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1272 // arrow from top to bottom
1273 pDC
->MoveTo(xMiddle
, rc
.top
);
1274 pDC
->LineTo(xMiddle
, rc
.bottom
-1);
1275 pDC
->LineTo(xMiddle
+4, rc
.bottom
-5);
1276 pDC
->MoveTo(xMiddle
, rc
.bottom
-1);
1277 pDC
->LineTo(xMiddle
-4, rc
.bottom
-5);
1280 pDC
->SelectObject(oldpen
);
1284 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1286 const int THICKNESS
= 2;
1287 COLORREF rectcol
= GetSysColor(m_bFocused
? COLOR_WINDOWTEXT
: COLOR_GRAYTEXT
);
1288 if ((nLineIndex
== m_nSelBlockStart
) && m_bShowSelection
)
1290 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1292 if ((nLineIndex
== m_nSelBlockEnd
) && m_bShowSelection
)
1294 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1298 void CBaseView::DrawText(
1299 CDC
* pDC
, const CRect
&rc
, LPCTSTR text
, int textlength
, int nLineIndex
, POINT coords
, bool bModified
, bool bInlineDiff
)
1301 ASSERT(m_pViewData
&& (nLineIndex
< m_pViewData
->GetCount()));
1302 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1304 // first suppose the whole line is selected
1305 int selectedStart
= 0, selectedEnd
= textlength
;
1307 if ((m_ptSelectionStartPos
.y
> nLineIndex
) || (m_ptSelectionEndPos
.y
< nLineIndex
)
1308 || ! m_bShowSelection
)
1310 // this line has no selected text
1311 selectedStart
= textlength
;
1313 else if ((m_ptSelectionStartPos
.y
== nLineIndex
) || (m_ptSelectionEndPos
.y
== nLineIndex
))
1315 // the line is partially selected
1316 int xoffs
= m_nOffsetChar
+ (coords
.x
- GetMarginWidth()) / GetCharWidth();
1317 if (m_ptSelectionStartPos
.y
== nLineIndex
)
1319 // the first line of selection
1320 int nSelectionStartOffset
= CalculateActualOffset(m_ptSelectionStartPos
.y
, m_ptSelectionStartPos
.x
);
1321 selectedStart
= max(min(nSelectionStartOffset
- xoffs
, textlength
), 0);
1324 if (m_ptSelectionEndPos
.y
== nLineIndex
)
1326 // the last line of selection
1327 int nSelectionEndOffset
= CalculateActualOffset(m_ptSelectionEndPos
.y
, m_ptSelectionEndPos
.x
);
1328 selectedEnd
= max(min(nSelectionEndOffset
- xoffs
, textlength
), 0);
1332 COLORREF crBkgnd
, crText
;
1333 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1334 if (bModified
|| (diffState
== DIFFSTATE_EDITED
))
1335 crBkgnd
= m_ModifiedBk
;
1337 crBkgnd
= InlineDiffColor(nLineIndex
);
1339 pDC
->SetBkColor(crBkgnd
);
1340 pDC
->SetTextColor(crText
);
1341 if (selectedStart
>=0)
1342 VERIFY(pDC
->ExtTextOut(coords
.x
, coords
.y
, ETO_CLIPPED
, &rc
, text
, selectedStart
, NULL
));
1344 long intenseColorScale
= m_bFocused
? 70 : 30;
1345 pDC
->SetBkColor(IntenseColor(intenseColorScale
, crBkgnd
));
1346 pDC
->SetTextColor(IntenseColor(intenseColorScale
, crText
));
1347 VERIFY(pDC
->ExtTextOut(
1348 coords
.x
+ selectedStart
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1349 text
+ selectedStart
, selectedEnd
- selectedStart
, NULL
));
1351 pDC
->SetBkColor(crBkgnd
);
1352 pDC
->SetTextColor(crText
);
1353 if (textlength
- selectedEnd
>= 0)
1354 VERIFY(pDC
->ExtTextOut(
1355 coords
.x
+ selectedEnd
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1356 text
+ selectedEnd
, textlength
- selectedEnd
, NULL
));
1359 bool CBaseView::DrawInlineDiff(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CString
&line
, CPoint
&origin
)
1361 if (!m_bShowInlineDiff
|| line
.IsEmpty())
1363 if ((m_pwndBottom
!= NULL
) && !(m_pwndBottom
->IsHidden()))
1366 LPCTSTR pszDiffChars
= NULL
;
1367 int nDiffLength
= 0;
1368 if (m_pOtherViewData
)
1370 int index
= min(nLineIndex
, m_pOtherViewData
->GetCount() - 1);
1371 pszDiffChars
= m_pOtherViewData
->GetLine(index
);
1372 nDiffLength
= m_pOtherViewData
->GetLine(index
).GetLength();
1375 if (!pszDiffChars
|| !*pszDiffChars
)
1379 ExpandChars(pszDiffChars
, 0, nDiffLength
, diffline
);
1380 svn_diff_t
* diff
= NULL
;
1381 m_svnlinediff
.Diff(&diff
, line
, line
.GetLength(), diffline
, diffline
.GetLength(), m_bInlineWordDiff
);
1382 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
1386 std::deque
<int> removedPositions
;
1389 apr_off_t len
= diff
->original_length
;
1392 for (int i
= 0; i
< len
; ++i
)
1394 s
+= m_svnlinediff
.m_line1tokens
[lineoffset
].c_str();
1397 bool isModified
= diff
->type
== svn_diff__type_diff_modified
;
1398 DrawText(pDC
, rc
, (LPCTSTR
)s
, s
.GetLength(), nLineIndex
, origin
, true, isModified
);
1399 origin
.x
+= pDC
->GetTextExtent(s
).cx
;
1401 if (isModified
&& (len
< diff
->modified_length
))
1402 removedPositions
.push_back(origin
.x
- 1);
1406 // Draw vertical bars at removed chunks' positions.
1407 for (std::deque
<int>::iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
1408 pDC
->FillSolidRect(*it
, rc
.top
, 1, rc
.Height(), m_InlineRemovedBk
);
1412 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1414 if (nLineIndex
>= GetLineCount())
1416 ASSERT(nLineIndex
>= -1);
1418 if ((nLineIndex
== -1) || !m_pViewData
)
1420 // Draw line beyond the text
1421 COLORREF crBkgnd
, crText
;
1422 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1423 pDC
->FillSolidRect(rc
, crBkgnd
);
1427 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1428 COLORREF crBkgnd
, crText
;
1429 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1431 if (diffState
== DIFFSTATE_CONFLICTED
)
1433 // conflicted lines are shown without 'text' on them
1435 pDC
->FillSolidRect(rc
, crBkgnd
);
1436 // now draw some faint text patterns
1437 pDC
->SetTextColor(IntenseColor(130, crBkgnd
));
1438 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
1439 DrawBlockLine(pDC
, rc
, nLineIndex
);
1443 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
1444 int nLength
= GetLineLength(nLineIndex
);
1447 // Draw the empty line
1448 pDC
->FillSolidRect(rc
, crBkgnd
);
1449 DrawBlockLine(pDC
, rc
, nLineIndex
);
1450 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1453 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
1454 if (pszChars
== NULL
)
1461 pDC
->SelectObject(GetFont(FALSE
, FALSE
, IsLineRemoved(nLineIndex
)));
1463 ExpandChars(pszChars
, 0, nLength
, line
);
1465 int nWidth
= rc
.right
- origin
.x
;
1466 int savedx
= origin
.x
;
1467 bool bInlineDiffDrawn
=
1468 nWidth
> 0 && diffState
!= DIFFSTATE_NORMAL
&&
1469 DrawInlineDiff(pDC
, rc
, nLineIndex
, line
, origin
);
1471 if (!bInlineDiffDrawn
)
1473 int nCount
= min(line
.GetLength(), nWidth
/ GetCharWidth() + 1);
1474 DrawText(pDC
, rc
, line
, nCount
, nLineIndex
, origin
, false, false);
1477 origin
.x
= savedx
+ pDC
->GetTextExtent(line
).cx
;
1479 // draw white space after the end of line
1481 if (origin
.x
> frect
.left
)
1482 frect
.left
= origin
.x
;
1483 if (bInlineDiffDrawn
)
1484 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1485 if (frect
.right
> frect
.left
)
1486 pDC
->FillSolidRect(frect
, crBkgnd
);
1487 // draw the whitespace chars
1488 if (m_bViewWhitespace
)
1491 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
1493 int nActualOffset
= 0;
1494 while ((nActualOffset
< m_nOffsetChar
) && (*pszChars
))
1496 if (*pszChars
== _T('\t'))
1497 nActualOffset
+= (GetTabSize() - nActualOffset
% GetTabSize());
1502 if (nActualOffset
> m_nOffsetChar
)
1505 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1506 CPen
pen2(PS_SOLID
, 2, m_WhiteSpaceFg
);
1514 CPen
* oldPen
= pDC
->SelectObject(&pen
);
1515 int nSpaces
= GetTabSize() - (m_nOffsetChar
+ xpos
) % GetTabSize();
1516 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
, y
);
1517 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1518 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
-4);
1519 pDC
->MoveTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1520 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
+4);
1522 pDC
->SelectObject(oldPen
);
1528 CPen
* oldPen
= pDC
->SelectObject(&pen2
);
1529 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2-1, y
);
1530 pDC
->LineTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2+1, y
);
1532 pDC
->SelectObject(oldPen
);
1542 DrawBlockLine(pDC
, rc
, nLineIndex
);
1543 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1546 void CBaseView::ExpandChars(LPCTSTR pszChars
, int nOffset
, int nCount
, CString
&line
)
1554 int nTabSize
= GetTabSize();
1556 int nActualOffset
= 0;
1557 for (int i
=0; i
<nOffset
; i
++)
1559 if (pszChars
[i
] == _T('\t'))
1560 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
1565 pszChars
+= nOffset
;
1566 int nLength
= nCount
;
1569 for (int i
=0; i
<nLength
; i
++)
1571 if (pszChars
[i
] == _T('\t'))
1575 LPTSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
1577 if (nTabCount
> 0 || m_bViewWhitespace
)
1579 for (int i
=0; i
<nLength
; i
++)
1581 if (pszChars
[i
] == _T('\t'))
1583 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
1586 pszBuf
[nCurPos
++] = _T(' ');
1592 pszBuf
[nCurPos
] = pszChars
[i
];
1599 memcpy(pszBuf
, pszChars
, sizeof(TCHAR
) * nLength
);
1602 pszBuf
[nCurPos
] = 0;
1603 line
.ReleaseBuffer();
1606 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
1608 if ((m_pwndLeft
)&&(m_pwndRight
))
1610 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1611 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1616 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1618 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1621 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1623 m_pwndLocator
->Invalidate();
1626 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
1628 //almost the same as ScrollAllToLine, but try to put the line in the
1629 //middle of the view, not on top
1630 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
1631 if (nNewTopLine
< 0)
1635 if (nNewTopLine
>= m_pViewData
->GetCount())
1636 nNewTopLine
= m_pViewData
->GetCount()-1;
1638 ScrollAllToLine(nNewTopLine
);
1640 ScrollToLine(nNewTopLine
);
1644 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
1649 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
1651 if (CView::OnCreate(lpCreateStruct
) == -1)
1654 memset(&m_lfBaseFont
, 0, sizeof(m_lfBaseFont
));
1655 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1656 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1657 m_lfBaseFont
.lfHeight
= 0;
1658 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
1659 m_lfBaseFont
.lfItalic
= FALSE
;
1660 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
1661 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1662 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1663 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
1664 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
1669 void CBaseView::OnDestroy()
1672 for (int i
=0; i
<MAXFONTS
; i
++)
1674 if (m_apFonts
[i
] != NULL
)
1676 m_apFonts
[i
]->DeleteObject();
1677 delete m_apFonts
[i
];
1678 m_apFonts
[i
] = NULL
;
1681 if (m_pCacheBitmap
!= NULL
)
1683 delete m_pCacheBitmap
;
1684 m_pCacheBitmap
= NULL
;
1688 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
1690 if (m_pCacheBitmap
!= NULL
)
1692 m_pCacheBitmap
->DeleteObject();
1693 delete m_pCacheBitmap
;
1694 m_pCacheBitmap
= NULL
;
1696 // make sure the view header is redrawn
1698 GetClientRect(&rcScroll
);
1699 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1700 InvalidateRect(&rcScroll
, FALSE
);
1702 m_nScreenLines
= -1;
1703 m_nScreenChars
= -1;
1704 RecalcVertScrollBar();
1705 RecalcHorzScrollBar();
1706 CView::OnSize(nType
, cx
, cy
);
1709 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
1712 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1714 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1716 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1718 m_pwndLocator
->Invalidate();
1719 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
1722 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
1724 if (GetKeyState(VK_CONTROL
)&0x8000)
1726 // Ctrl-Wheel scrolls sideways
1727 ScrollSide(-zDelta
/30);
1731 int nLineCount
= GetLineCount();
1732 int nTopLine
= m_nTopLine
;
1733 nTopLine
-= (zDelta
/30);
1736 if (nTopLine
>= nLineCount
)
1737 nTopLine
= nLineCount
- 1;
1738 ScrollToLine(nTopLine
, TRUE
);
1742 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1744 if (nHitTest
== HTCLIENT
)
1746 ::SetCursor(::LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
))); // Set To Arrow Cursor
1749 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
1752 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
1754 CView::OnKillFocus(pNewWnd
);
1760 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
1762 CView::OnSetFocus(pOldWnd
);
1768 int CBaseView::GetLineFromPoint(CPoint point
)
1770 ScreenToClient(&point
);
1771 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
1774 bool CBaseView::OnContextMenu(CPoint
/*point*/, int /*nLine*/, DiffStates
/*state*/)
1779 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
1781 int nLine
= GetLineFromPoint(point
);
1785 if (m_nSelBlockEnd
>= GetLineCount())
1786 m_nSelBlockEnd
= GetLineCount()-1;
1787 if ((nLine
<= m_pViewData
->GetCount())&&(nLine
> m_nTopLine
))
1789 int nIndex
= nLine
- 1;
1790 DiffStates state
= m_pViewData
->GetState(nIndex
);
1791 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1793 // if there's nothing selected, or if the selection is outside the window then
1794 // select the diff block under the cursor.
1795 if (((m_nSelBlockStart
<0)&&(m_nSelBlockEnd
<0))||
1796 ((m_nSelBlockEnd
< m_nTopLine
)||(m_nSelBlockStart
> m_nTopLine
+m_nScreenLines
)))
1805 if (state
!= m_pViewData
->GetState(--nIndex
))
1808 m_nSelBlockStart
= nIndex
+1;
1809 while (nIndex
< (m_pViewData
->GetCount()-1))
1811 if (state
!= m_pViewData
->GetState(++nIndex
))
1814 if ((nIndex
== (m_pViewData
->GetCount()-1))&&(state
== m_pViewData
->GetState(nIndex
)))
1815 m_nSelBlockEnd
= nIndex
;
1817 m_nSelBlockEnd
= nIndex
-1;
1818 SetupSelection(m_nSelBlockStart
, m_nSelBlockEnd
);
1820 m_ptCaretPos
.y
= nLine
- 1;
1824 if (((state
== DIFFSTATE_NORMAL
)||(state
== DIFFSTATE_UNKNOWN
)) &&
1825 (m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))
1827 // find a more 'relevant' state in the selection
1828 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; ++i
)
1830 state
= m_pViewData
->GetState(i
);
1831 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1835 bool bKeepSelection
= OnContextMenu(point
, nLine
, state
);
1836 if (! bKeepSelection
)
1842 void CBaseView::RefreshViews()
1846 m_pwndLeft
->UpdateStatusBar();
1847 m_pwndLeft
->Invalidate();
1851 m_pwndRight
->UpdateStatusBar();
1852 m_pwndRight
->Invalidate();
1856 m_pwndBottom
->UpdateStatusBar();
1857 m_pwndBottom
->Invalidate();
1860 m_pwndLocator
->Invalidate();
1863 void CBaseView::GoToFirstDifference()
1866 SelectNextBlock(1, false, false);
1869 void CBaseView::HiglightLines(int start
, int end
/* = -1 */)
1872 m_nSelBlockStart
= start
;
1875 m_nSelBlockEnd
= end
;
1877 m_ptCaretPos
.y
= start
;
1882 void CBaseView::SetupSelection(int start
, int end
)
1884 if (IsBottomViewGood())
1886 m_pwndBottom
->m_nSelBlockStart
= start
;
1887 m_pwndBottom
->m_nSelBlockEnd
= end
;
1888 m_pwndBottom
->Invalidate();
1890 if (IsLeftViewGood())
1892 m_pwndLeft
->m_nSelBlockStart
= start
;
1893 m_pwndLeft
->m_nSelBlockEnd
= end
;
1894 m_pwndLeft
->Invalidate();
1896 if (IsRightViewGood())
1898 m_pwndRight
->m_nSelBlockStart
= start
;
1899 m_pwndRight
->m_nSelBlockEnd
= end
;
1900 m_pwndRight
->Invalidate();
1904 void CBaseView::OnMergePreviousconflict()
1906 SelectNextBlock(-1, true);
1909 void CBaseView::OnMergeNextconflict()
1911 SelectNextBlock(1, true);
1914 void CBaseView::OnMergeNextdifference()
1916 SelectNextBlock(1, false);
1919 void CBaseView::OnMergePreviousdifference()
1921 SelectNextBlock(-1, false);
1924 void CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */)
1929 if (m_pViewData
->GetCount() == 0)
1932 int nCenterPos
= m_ptCaretPos
.y
;
1935 nLimit
= m_pViewData
->GetCount() - 1;
1937 if (nCenterPos
>= m_pViewData
->GetCount())
1938 nCenterPos
= m_pViewData
->GetCount()-1;
1940 if (bSkipEndOfCurrentBlock
)
1942 // Find end of current block
1943 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1944 while ((nCenterPos
!= nLimit
) &&
1945 (m_pViewData
->GetState(nCenterPos
)==state
))
1946 nCenterPos
+= nDirection
;
1949 // Find next diff/conflict block
1950 while (nCenterPos
!= nLimit
)
1952 DiffStates linestate
= m_pViewData
->GetState(nCenterPos
);
1954 (linestate
!= DIFFSTATE_NORMAL
) &&
1955 (linestate
!= DIFFSTATE_UNKNOWN
))
1958 ((linestate
== DIFFSTATE_CONFLICTADDED
) ||
1959 (linestate
== DIFFSTATE_CONFLICTED_IGNORED
) ||
1960 (linestate
== DIFFSTATE_CONFLICTED
) ||
1961 (linestate
== DIFFSTATE_CONFLICTEMPTY
)))
1964 nCenterPos
+= nDirection
;
1967 // Find end of new block
1968 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1969 int nBlockEnd
= nCenterPos
;
1970 while ((nBlockEnd
!= nLimit
) &&
1971 (state
== m_pViewData
->GetState(nBlockEnd
+ nDirection
)))
1972 nBlockEnd
+= nDirection
;
1974 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
1979 m_ptCaretPos
.y
= nCenterPos
;
1982 SetupSelection(nCenterPos
, nBlockEnd
);
1984 SetupSelection(nBlockEnd
, nCenterPos
);
1986 ScrollAllToLine(nTopPos
, FALSE
);
1987 RecalcAllVertScrollBars(TRUE
);
1988 m_nCaretGoalPos
= 0;
1990 ShowDiffLines(nCenterPos
);
1993 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
1995 // need to handle both ANSI and UNICODE versions of the message
1996 TOOLTIPTEXTA
* pTTTA
= (TOOLTIPTEXTA
*)pNMHDR
;
1997 TOOLTIPTEXTW
* pTTTW
= (TOOLTIPTEXTW
*)pNMHDR
;
1999 UINT nID
= (UINT
)pNMHDR
->idFrom
;
2000 if (pNMHDR
->code
== TTN_NEEDTEXTA
&& (pTTTA
->uFlags
& TTF_IDISHWND
) ||
2001 pNMHDR
->code
== TTN_NEEDTEXTW
&& (pTTTW
->uFlags
& TTF_IDISHWND
))
2003 // idFrom is actually the HWND of the tool
2004 nID
= ::GetDlgCtrlID((HWND
)nID
);
2007 if (pNMHDR
->idFrom
== (UINT
)m_hWnd
)
2009 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
2011 strTipText
= m_sWindowName
.Mid(2) + _T("\r\n") + m_sFullFilePath
;
2015 strTipText
= m_sWindowName
+ _T("\r\n") + m_sFullFilePath
;
2022 if (strTipText
.IsEmpty())
2025 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2027 pTTTA
->lpszText
= m_szTip
;
2028 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2032 lstrcpyn(m_wszTip
, strTipText
, strTipText
.GetLength()+1);
2033 pTTTW
->lpszText
= m_wszTip
;
2036 return TRUE
; // message was handled
2040 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2043 GetClientRect(rcClient
);
2044 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2045 if (textrect
.PtInRect(point
))
2047 // inside the header part of the view (showing the filename)
2048 pTI
->hwnd
= this->m_hWnd
;
2049 this->GetClientRect(&pTI
->rect
);
2050 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
2051 pTI
->uId
= (UINT
)m_hWnd
;
2052 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
2054 // we want multi line tooltips
2055 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
2056 if (pToolTip
->GetSafeHwnd() != NULL
)
2058 pToolTip
->SetMaxTipWidth(INT_MAX
);
2066 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2068 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2069 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2074 m_ptCaretPos
.y
-= GetScreenLines();
2075 m_ptCaretPos
.y
= max(m_ptCaretPos
.y
, 0);
2076 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2082 EnsureCaretVisible();
2083 ShowDiffLines(m_ptCaretPos
.y
);
2088 m_ptCaretPos
.y
+= GetScreenLines();
2089 if (m_ptCaretPos
.y
>= GetLineCount())
2090 m_ptCaretPos
.y
= GetLineCount()-1;
2091 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2097 EnsureCaretVisible();
2098 ShowDiffLines(m_ptCaretPos
.y
);
2108 m_nCaretGoalPos
= 0;
2118 m_nCaretGoalPos
= 0;
2123 EnsureCaretVisible();
2132 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2133 m_ptCaretPos
.y
= GetLineCount()-1;
2134 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2144 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2150 EnsureCaretVisible();
2160 if (! HasTextSelection()) {
2161 if (m_ptCaretPos
.y
== 0 && m_ptCaretPos
.x
== 0)
2163 m_ptSelectionEndPos
= m_ptCaretPos
;
2165 m_ptSelectionStartPos
= m_ptCaretPos
;
2167 RemoveSelectedText();
2175 if (! HasTextSelection()) {
2176 if (! MoveCaretRight())
2178 m_ptSelectionEndPos
= m_ptCaretPos
;
2180 m_ptSelectionStartPos
= m_ptCaretPos
;
2182 RemoveSelectedText();
2186 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
2189 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
2191 int nClickedLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2192 nClickedLine
--; //we need the index
2193 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
2195 m_ptCaretPos
.y
= nClickedLine
;
2196 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2199 if (nFlags
& MK_SHIFT
)
2204 SetupSelection(m_ptCaretPos
.y
, m_ptCaretPos
.y
);
2212 CView::OnLButtonDown(nFlags
, point
);
2215 void CBaseView::OnEditCopy()
2217 if ((m_ptSelectionStartPos
.x
== m_ptSelectionEndPos
.x
)&&(m_ptSelectionStartPos
.y
== m_ptSelectionEndPos
.y
))
2219 // first store the selected lines in one CString
2221 for (int i
=m_ptSelectionStartPos
.y
; i
<=m_ptSelectionEndPos
.y
; i
++)
2223 switch (m_pViewData
->GetState(i
))
2225 case DIFFSTATE_EMPTY
:
2227 case DIFFSTATE_UNKNOWN
:
2228 case DIFFSTATE_NORMAL
:
2229 case DIFFSTATE_REMOVED
:
2230 case DIFFSTATE_REMOVEDWHITESPACE
:
2231 case DIFFSTATE_ADDED
:
2232 case DIFFSTATE_ADDEDWHITESPACE
:
2233 case DIFFSTATE_WHITESPACE
:
2234 case DIFFSTATE_WHITESPACE_DIFF
:
2235 case DIFFSTATE_CONFLICTED
:
2236 case DIFFSTATE_CONFLICTED_IGNORED
:
2237 case DIFFSTATE_CONFLICTADDED
:
2238 case DIFFSTATE_CONFLICTEMPTY
:
2239 case DIFFSTATE_CONFLICTRESOLVED
:
2240 case DIFFSTATE_IDENTICALREMOVED
:
2241 case DIFFSTATE_IDENTICALADDED
:
2242 case DIFFSTATE_THEIRSREMOVED
:
2243 case DIFFSTATE_THEIRSADDED
:
2244 case DIFFSTATE_YOURSREMOVED
:
2245 case DIFFSTATE_YOURSADDED
:
2246 case DIFFSTATE_EDITED
:
2247 sCopyData
+= m_pViewData
->GetLine(i
);
2248 sCopyData
+= _T("\r\n");
2252 // remove the last \r\n
2253 sCopyData
= sCopyData
.Left(sCopyData
.GetLength()-2);
2254 // remove the non-selected chars from the first line
2255 sCopyData
= sCopyData
.Mid(m_ptSelectionStartPos
.x
);
2256 // remove the non-selected chars from the last line
2257 int lastLinePos
= sCopyData
.ReverseFind('\n');
2259 if (lastLinePos
== 0)
2260 lastLinePos
-= m_ptSelectionStartPos
.x
;
2261 sCopyData
= sCopyData
.Left(lastLinePos
+m_ptSelectionEndPos
.x
);
2262 if (!sCopyData
.IsEmpty())
2264 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
2268 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
2270 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0) {
2271 --m_pMainFrame
->m_nMoveMovesToIgnore
;
2272 CView::OnMouseMove(nFlags
, point
);
2276 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2277 nMouseLine
--; //we need the index
2278 if (nMouseLine
< -1)
2282 ShowDiffLines(nMouseLine
);
2284 KillTimer(IDT_SCROLLTIMER
);
2285 if (nFlags
& MK_LBUTTON
)
2287 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2288 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2289 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2290 if (((m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))&&
2291 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
2293 m_ptCaretPos
.y
= nMouseLine
;
2294 m_ptCaretPos
.x
= charIndex
;
2301 if (nMouseLine
< m_nTopLine
)
2303 ScrollToLine(m_nTopLine
-1, TRUE
);
2304 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2306 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2308 ScrollToLine(m_nTopLine
+1, TRUE
);
2309 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2311 if (charIndex
<= m_nOffsetChar
)
2314 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2316 if (charIndex
>= (GetScreenChars()+m_nOffsetChar
))
2319 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2323 if (!m_bMouseWithin
)
2325 m_bMouseWithin
= TRUE
;
2326 TRACKMOUSEEVENT tme
;
2327 tme
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2328 tme
.dwFlags
= TME_LEAVE
;
2329 tme
.hwndTrack
= m_hWnd
;
2330 _TrackMouseEvent(&tme
);
2333 CView::OnMouseMove(nFlags
, point
);
2336 void CBaseView::OnMouseLeave()
2339 m_bMouseWithin
= FALSE
;
2340 KillTimer(IDT_SCROLLTIMER
);
2341 CView::OnMouseLeave();
2344 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
2346 if (nIDEvent
== IDT_SCROLLTIMER
)
2349 GetCursorPos(&point
);
2350 ScreenToClient(&point
);
2351 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2352 nMouseLine
--; //we need the index
2353 if (nMouseLine
< -1)
2357 if (GetKeyState(VK_LBUTTON
)&0x8000)
2359 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2360 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2361 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2362 if (nMouseLine
< m_nTopLine
)
2364 ScrollToLine(m_nTopLine
-1, TRUE
);
2365 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2367 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2369 ScrollToLine(m_nTopLine
+1, TRUE
);
2370 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2372 if (charIndex
<= m_nOffsetChar
)
2375 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2377 if (charIndex
>= GetScreenChars())
2380 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2386 CView::OnTimer(nIDEvent
);
2389 void CBaseView::SelectLines(int nLine1
, int nLine2
)
2393 m_nSelBlockStart
= nLine1
;
2394 m_nSelBlockEnd
= nLine2
;
2398 void CBaseView::ShowDiffLines(int nLine
)
2400 if ((nLine
>= m_nTopLine
)&&(nLine
< GetLineCount()))
2402 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
)&&(m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
)&&(!m_pMainFrame
->m_bOneWay
))
2404 nLine
= (nLine
> m_pwndRight
->m_pViewData
->GetCount() ? -1 : nLine
);
2405 nLine
= (nLine
> m_pwndLeft
->m_pViewData
->GetCount() ? -1 : nLine
);
2409 if (nLine
!= m_nMouseLine
)
2411 m_nMouseLine
= nLine
;
2412 if (nLine
>= GetLineCount())
2414 m_pwndLineDiffBar
->ShowLines(nLine
);
2421 m_pwndLineDiffBar
->ShowLines(nLine
);
2425 void CBaseView::UseTheirAndYourBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2427 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2429 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2431 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2432 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndLeft
->m_pViewData
->GetLine(i
));
2433 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2434 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndLeft
->m_pViewData
->GetState(i
));
2435 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, m_pwndBottom
->lineendings
);
2436 if (m_pwndBottom
->IsLineConflicted(i
))
2438 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2439 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2441 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2443 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2446 // your block is done, now insert their block
2447 int index
= m_nSelBlockEnd
+1;
2448 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2450 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2451 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndRight
->m_pViewData
->GetData(i
));
2452 if (m_pwndBottom
->IsLineConflicted(index
))
2454 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2455 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2457 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2459 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2462 // adjust line numbers
2463 for (int i
=m_nSelBlockEnd
+1; i
<GetLineCount(); ++i
)
2465 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2467 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2470 // now insert an empty block in both yours and theirs
2471 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2473 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2474 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2475 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2476 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2478 RecalcAllVertScrollBars();
2479 m_pwndBottom
->SetModified();
2480 m_pwndLeft
->SetModified();
2481 m_pwndRight
->SetModified();
2484 void CBaseView::UseYourAndTheirBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2486 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2488 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2490 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2491 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndRight
->m_pViewData
->GetLine(i
));
2492 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2493 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndRight
->m_pViewData
->GetState(i
));
2494 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2495 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, m_pwndBottom
->lineendings
);
2496 if (m_pwndBottom
->IsLineConflicted(i
))
2498 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2499 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2501 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2503 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2506 // your block is done, now insert their block
2507 int index
= m_nSelBlockEnd
+1;
2508 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2510 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2511 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2512 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2513 if (m_pwndBottom
->IsLineConflicted(index
))
2515 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2516 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2518 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2520 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2523 // adjust line numbers
2524 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndBottom
->GetLineCount(); ++i
)
2526 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2528 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2531 // now insert an empty block in both yours and theirs
2532 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2534 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2535 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2536 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2537 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2540 RecalcAllVertScrollBars();
2541 m_pwndBottom
->SetModified();
2542 m_pwndLeft
->SetModified();
2543 m_pwndRight
->SetModified();
2546 void CBaseView::UseBothRightFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2548 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2550 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2552 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2553 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2556 // your block is done, now insert their block
2557 int index
= m_nSelBlockEnd
+1;
2558 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2560 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2561 m_pwndRight
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2562 m_pwndRight
->m_pViewData
->SetState(index
++, DIFFSTATE_THEIRSADDED
);
2564 // adjust line numbers
2566 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2568 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2570 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2573 // now insert an empty block in the left view
2574 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2576 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2577 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2579 RecalcAllVertScrollBars();
2580 m_pwndLeft
->SetModified();
2581 m_pwndRight
->SetModified();
2584 void CBaseView::UseBothLeftFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2586 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2588 // get line number from just before the block
2589 long linenumber
= 0;
2590 if (m_nSelBlockStart
> 0)
2591 linenumber
= m_pwndRight
->m_pViewData
->GetLineNumber(m_nSelBlockStart
-1);
2593 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2595 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2596 m_pwndRight
->m_pViewData
->InsertData(i
, m_pwndLeft
->m_pViewData
->GetLine(i
), DIFFSTATE_THEIRSADDED
, linenumber
++, m_pwndLeft
->m_pViewData
->GetLineEnding(i
));
2598 // adjust line numbers
2599 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2601 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2603 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(m_nSelBlockEnd
-m_nSelBlockStart
)+1);
2606 // now insert an empty block in left view
2607 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2609 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+ 1);
2610 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+ 1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2612 RecalcAllVertScrollBars();
2613 m_pwndLeft
->SetModified();
2614 m_pwndRight
->SetModified();
2617 void CBaseView::UpdateCaret()
2619 if (m_ptCaretPos
.y
>= GetLineCount())
2620 m_ptCaretPos
.y
= GetLineCount()-1;
2621 if (m_ptCaretPos
.y
< 0)
2623 if (m_ptCaretPos
.x
> GetLineLength(m_ptCaretPos
.y
))
2624 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2625 if (m_ptCaretPos
.x
< 0)
2628 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2630 if (m_bFocused
&& !m_bCaretHidden
&&
2631 m_ptCaretPos
.y
>= m_nTopLine
&&
2632 m_ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
2633 nCaretOffset
>= m_nOffsetChar
&&
2634 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
2636 CreateSolidCaret(2, GetLineHeight());
2637 SetCaretPos(TextToClient(m_ptCaretPos
));
2646 void CBaseView::EnsureCaretVisible()
2648 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2650 if (m_ptCaretPos
.y
< m_nTopLine
)
2651 ScrollAllToLine(m_ptCaretPos
.y
);
2652 if (m_ptCaretPos
.y
>= (m_nTopLine
+GetScreenLines()))
2653 ScrollAllToLine(m_ptCaretPos
.y
-GetScreenLines()+1);
2654 if (nCaretOffset
< m_nOffsetChar
)
2655 ScrollToChar(nCaretOffset
);
2656 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
2657 ScrollToChar(nCaretOffset
-GetScreenChars()+1);
2660 int CBaseView::CalculateActualOffset(int nLineIndex
, int nCharIndex
) const
2662 int nLength
= GetLineLength(nLineIndex
);
2663 ASSERT(nCharIndex
>= 0);
2664 if (nCharIndex
> nLength
)
2665 nCharIndex
= nLength
;
2666 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
2668 int nTabSize
= GetTabSize();
2669 for (int I
= 0; I
< nCharIndex
; I
++)
2671 if (pszChars
[I
] == _T('\t'))
2672 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2679 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
) const
2681 int nLength
= GetLineLength(nLineIndex
);
2682 LPCTSTR pszLine
= GetLineChars(nLineIndex
);
2685 int nTabSize
= GetTabSize();
2686 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
2688 if (pszLine
[nIndex
] == _T('\t'))
2689 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2697 POINT
CBaseView::TextToClient(const POINT
& point
)
2700 pt
.y
= max(0, (point
.y
- m_nTopLine
) * GetLineHeight());
2701 pt
.x
= CalculateActualOffset(point
.y
, point
.x
);
2703 pt
.x
= (pt
.x
- m_nOffsetChar
) * GetCharWidth() + GetMarginWidth();
2704 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
2708 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2710 CView::OnChar(nChar
, nRepCnt
, nFlags
);
2715 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
2716 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
2719 if ((nChar
> 31)||(nChar
== VK_TAB
))
2721 RemoveSelectedText();
2722 AddUndoLine(m_ptCaretPos
.y
);
2723 CString sLine
= GetLineChars(m_ptCaretPos
.y
);
2724 sLine
.Insert(m_ptCaretPos
.x
, (wchar_t)nChar
);
2725 m_pViewData
->SetLine(m_ptCaretPos
.y
, sLine
);
2726 m_pViewData
->SetState(m_ptCaretPos
.y
, DIFFSTATE_EDITED
);
2727 if (m_pViewData
->GetLineEnding(m_ptCaretPos
.y
) == EOL_NOENDING
&& m_pViewData
->GetCount() - 1 != m_ptCaretPos
.y
)
2728 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, lineendings
);
2732 else if (nChar
== 10)
2734 AddUndoLine(m_ptCaretPos
.y
);
2735 EOL eol
= m_pViewData
->GetLineEnding(m_ptCaretPos
.y
);
2736 EOL newEOL
= EOL_CRLF
;
2745 newEOL
= (m_pViewData
->GetCount() - 1 == m_ptCaretPos
.y
&& !m_pViewData
->GetLine(m_ptCaretPos
.y
).IsEmpty()) ? EOL_NOENDING
: EOL_CRLF
;
2748 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, newEOL
);
2749 m_pViewData
->SetState(m_ptCaretPos
.y
, DIFFSTATE_EDITED
);
2752 else if (nChar
== VK_RETURN
)
2754 // insert a new, fresh and empty line below the cursor
2755 RemoveSelectedText();
2756 AddUndoLine(m_ptCaretPos
.y
, true);
2757 EOL eOriginalEnding
= m_pViewData
->GetLineEnding(m_ptCaretPos
.y
);
2758 if (m_pViewData
->GetCount() - 2 == m_ptCaretPos
.y
&& eOriginalEnding
== EOL_NOENDING
)
2759 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, lineendings
);
2760 // move the cursor to the new line
2763 if (m_pViewData
->GetCount() - 1 == m_ptCaretPos
.y
)
2764 m_pViewData
->SetLineEnding(m_ptCaretPos
.y
, eOriginalEnding
);
2768 return; // Unknown control character -- ignore it.
2770 EnsureCaretVisible();
2776 void CBaseView::AddUndoLine(int nLine
, bool bAddEmptyLine
)
2778 viewstate leftstate
;
2779 viewstate rightstate
;
2780 viewstate bottomstate
;
2781 leftstate
.AddLineFormView(m_pwndLeft
, nLine
, bAddEmptyLine
);
2782 rightstate
.AddLineFormView(m_pwndRight
, nLine
, bAddEmptyLine
);
2783 bottomstate
.AddLineFormView(m_pwndBottom
, nLine
, bAddEmptyLine
);
2784 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2787 void CBaseView::AddEmptyLine(int nLineIndex
)
2789 if (m_pViewData
== NULL
)
2791 if (!m_bCaretHidden
)
2793 CString sPartLine
= GetLineChars(nLineIndex
);
2794 m_pViewData
->SetLine(nLineIndex
, sPartLine
.Left(m_ptCaretPos
.x
));
2795 sPartLine
= sPartLine
.Mid(m_ptCaretPos
.x
);
2796 m_pViewData
->InsertData(nLineIndex
+1, sPartLine
, DIFFSTATE_EDITED
, -1, lineendings
);
2799 m_pViewData
->InsertData(nLineIndex
+1, _T(""), DIFFSTATE_EDITED
, -1, lineendings
);
2803 void CBaseView::RemoveLine(int nLineIndex
)
2805 if (m_pViewData
== NULL
)
2807 m_pViewData
->RemoveData(nLineIndex
);
2808 if (m_ptCaretPos
.y
>= GetLineCount())
2809 m_ptCaretPos
.y
= GetLineCount()-1;
2813 void CBaseView::RemoveSelectedText()
2815 if (m_pViewData
== NULL
)
2817 if (!HasTextSelection())
2820 viewstate rightstate
;
2821 viewstate bottomstate
;
2822 viewstate leftstate
;
2823 std::vector
<LONG
> linestoremove
;
2824 for (LONG i
= m_ptSelectionStartPos
.y
; i
<= m_ptSelectionEndPos
.y
; ++i
)
2826 if (i
== m_ptSelectionStartPos
.y
)
2828 CString sLine
= GetLineChars(m_ptSelectionStartPos
.y
);
2830 if (i
== m_ptSelectionStartPos
.y
)
2832 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2834 leftstate
.difflines
[i
] = m_pwndLeft
->m_pViewData
->GetLine(i
);
2835 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2837 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2839 rightstate
.difflines
[i
] = m_pwndRight
->m_pViewData
->GetLine(i
);
2840 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2842 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2844 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2845 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2847 newLine
= sLine
.Left(m_ptSelectionStartPos
.x
);
2848 sLine
= GetLineChars(m_ptSelectionEndPos
.y
);
2849 newLine
= newLine
+ sLine
.Mid(m_ptSelectionEndPos
.x
);
2851 m_pViewData
->SetLine(i
, newLine
);
2852 m_pViewData
->SetState(i
, DIFFSTATE_EDITED
);
2857 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2859 leftstate
.removedlines
[i
] = m_pwndLeft
->m_pViewData
->GetData(i
);
2861 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2863 rightstate
.removedlines
[i
] = m_pwndRight
->m_pViewData
->GetData(i
);
2865 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2867 bottomstate
.removedlines
[i
] = m_pwndBottom
->m_pViewData
->GetData(i
);
2869 linestoremove
.push_back(i
);
2872 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2873 // remove the lines at the end, to avoid problems with line indexes
2874 if (!linestoremove
.empty())
2876 std::vector
<LONG
>::const_iterator it
= linestoremove
.begin();
2877 int nLineToRemove
= *it
;
2878 for ( ; it
!= linestoremove
.end(); ++it
)
2881 m_pwndLeft
->RemoveLine(nLineToRemove
);
2883 m_pwndRight
->RemoveLine(nLineToRemove
);
2885 m_pwndBottom
->RemoveLine(nLineToRemove
);
2889 m_ptCaretPos
= m_ptSelectionStartPos
;
2893 EnsureCaretVisible();
2897 void CBaseView::PasteText()
2899 if (!OpenClipboard())
2902 CString sClipboardText
;
2903 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
2906 LPCSTR lpstr
= (LPCSTR
)GlobalLock(hglb
);
2907 sClipboardText
= CString(lpstr
);
2910 hglb
= GetClipboardData(CF_UNICODETEXT
);
2913 LPCTSTR lpstr
= (LPCTSTR
)GlobalLock(hglb
);
2914 sClipboardText
= lpstr
;
2919 if (sClipboardText
.IsEmpty())
2922 sClipboardText
.Replace(_T("\r\n"), _T("\r"));
2923 sClipboardText
.Replace('\n', '\r');
2924 // We want to undo the insertion in a single step.
2925 CUndo::GetInstance().BeginGrouping();
2926 // use the easy way to insert text:
2927 // insert char by char, using the OnChar() method
2928 for (int i
=0; i
<sClipboardText
.GetLength(); ++i
)
2930 OnChar(sClipboardText
[i
], 0, 0);
2932 CUndo::GetInstance().EndGrouping();
2935 void CBaseView::OnCaretDown()
2938 m_ptCaretPos
.y
= min(m_ptCaretPos
.y
, GetLineCount()-1);
2939 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2940 if (GetKeyState(VK_SHIFT
)&0x8000)
2945 EnsureCaretVisible();
2946 ShowDiffLines(m_ptCaretPos
.y
);
2949 bool CBaseView::MoveCaretLeft()
2951 if (m_ptCaretPos
.x
== 0)
2953 if (m_ptCaretPos
.y
> 0)
2956 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2968 bool CBaseView::MoveCaretRight()
2970 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
2972 if (m_ptCaretPos
.y
< (GetLineCount() - 1))
2987 void CBaseView::UpdateGoalPos()
2989 m_nCaretGoalPos
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2992 void CBaseView::OnCaretLeft()
2995 if (GetKeyState(VK_SHIFT
)&0x8000)
2999 EnsureCaretVisible();
3003 void CBaseView::OnCaretRight()
3006 if (GetKeyState(VK_SHIFT
)&0x8000)
3010 EnsureCaretVisible();
3014 void CBaseView::OnCaretUp()
3017 m_ptCaretPos
.y
= max(0, m_ptCaretPos
.y
);
3018 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
3019 if (GetKeyState(VK_SHIFT
)&0x8000)
3024 EnsureCaretVisible();
3025 ShowDiffLines(m_ptCaretPos
.y
);
3028 bool CBaseView::IsWordSeparator(wchar_t ch
) const
3030 return ch
== ' ' || ch
== '\t' || (m_sWordSeparators
.Find(ch
) >= 0);
3033 bool CBaseView::IsCaretAtWordBoundary() const
3035 LPCTSTR line
= GetLineChars(m_ptCaretPos
.y
);
3037 return false; // no boundary at the empty lines
3038 if (m_ptCaretPos
.x
== 0)
3039 return !IsWordSeparator(line
[m_ptCaretPos
.x
]);
3040 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
3041 return !IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3043 IsWordSeparator(line
[m_ptCaretPos
.x
]) !=
3044 IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3047 void CBaseView::OnCaretWordleft()
3049 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3052 if (GetKeyState(VK_SHIFT
)&0x8000)
3056 EnsureCaretVisible();
3060 void CBaseView::OnCaretWordright()
3062 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3065 if (GetKeyState(VK_SHIFT
)&0x8000)
3069 EnsureCaretVisible();
3073 void CBaseView::ClearCurrentSelection()
3075 m_ptSelectionStartPos
= m_ptCaretPos
;
3076 m_ptSelectionEndPos
= m_ptCaretPos
;
3077 m_ptSelectionOrigin
= m_ptCaretPos
;
3078 m_nSelBlockStart
= -1;
3079 m_nSelBlockEnd
= -1;
3083 void CBaseView::ClearSelection()
3086 m_pwndLeft
->ClearCurrentSelection();
3088 m_pwndRight
->ClearCurrentSelection();
3090 m_pwndBottom
->ClearCurrentSelection();
3093 void CBaseView::AdjustSelection()
3095 if ((m_ptCaretPos
.y
< m_ptSelectionOrigin
.y
) ||
3096 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
<= m_ptSelectionOrigin
.x
))
3098 m_ptSelectionStartPos
= m_ptCaretPos
;
3099 m_ptSelectionEndPos
= m_ptSelectionOrigin
;
3102 if ((m_ptCaretPos
.y
> m_ptSelectionOrigin
.y
) ||
3103 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
>= m_ptSelectionOrigin
.x
))
3105 m_ptSelectionStartPos
= m_ptSelectionOrigin
;
3106 m_ptSelectionEndPos
= m_ptCaretPos
;
3109 SetupSelection(min(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
), max(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
));
3114 void CBaseView::OnEditCut()
3116 if (!m_bCaretHidden
)
3119 RemoveSelectedText();
3123 void CBaseView::OnEditPaste()
3125 if (!m_bCaretHidden
)
3131 void CBaseView::OnEditSelectall()
3133 int nCount
= GetLineCount();
3134 SetupSelection(0, nCount
);
3135 m_ptSelectionStartPos
.x
= 0;
3136 m_ptSelectionStartPos
.y
= 0;
3138 m_ptSelectionEndPos
.y
= nCount
-1;
3139 CString sLine
= GetLineChars(nCount
-1);
3140 m_ptSelectionEndPos
.x
= sLine
.GetLength();