1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2003-2009 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "TortoiseMerge.h"
25 #include "DiffColors.h"
26 #include "StringUtils.h"
34 #define MARGINWIDTH 20
35 #define HEADERHEIGHT 10
39 #define INLINEADDED_COLOR RGB(255, 255, 150)
40 #define INLINEREMOVED_COLOR RGB(200, 100, 100)
41 #define MODIFIED_COLOR RGB(220, 220, 255)
43 #define IDT_SCROLLTIMER 101
45 CBaseView
* CBaseView::m_pwndLeft
= NULL
;
46 CBaseView
* CBaseView::m_pwndRight
= NULL
;
47 CBaseView
* CBaseView::m_pwndBottom
= NULL
;
48 CLocatorBar
* CBaseView::m_pwndLocator
= NULL
;
49 CLineDiffBar
* CBaseView::m_pwndLineDiffBar
= NULL
;
50 CMFCStatusBar
* CBaseView::m_pwndStatusBar
= NULL
;
51 CMainFrame
* CBaseView::m_pMainFrame
= NULL
;
53 IMPLEMENT_DYNCREATE(CBaseView
, CView
)
55 CBaseView::CBaseView()
57 m_pCacheBitmap
= NULL
;
59 m_pOtherViewData
= NULL
;
63 m_nMaxLineLength
= -1;
69 m_bMouseWithin
= FALSE
;
71 lineendings
= EOL_AUTOLINE
;
72 m_bCaretHidden
= true;
76 m_ptSelectionStartPos
= m_ptCaretPos
;
77 m_ptSelectionEndPos
= m_ptCaretPos
;
78 m_ptSelectionOrigin
= m_ptCaretPos
;
80 m_bShowSelection
= true;
81 texttype
= CFileTextLines::AUTOTYPE
;
82 m_bViewWhitespace
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewWhitespaces"), 1);
83 m_bViewLinenumbers
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
84 m_bShowInlineDiff
= CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE
);
85 m_InlineAddedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR
);
86 m_InlineRemovedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR
);
87 m_ModifiedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR
);
88 m_WhiteSpaceFg
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT
));
89 m_sWordSeparators
= CRegString(_T("Software\\TortoiseMerge\\WordSeparators"), _T("[]();.,{}!@#$%^&*-+=|/\\<>'`~"));;
90 m_bIconLFs
= CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
91 m_nSelBlockStart
= -1;
94 m_bOtherDiffChecked
= false;
95 m_bInlineWordDiff
= true;
96 m_nTabSize
= (int)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
97 for (int i
=0; i
<MAXFONTS
; i
++)
101 m_hConflictedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDLINE
),
102 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
103 m_hConflictedIgnoredIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDIGNOREDLINE
),
104 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
105 m_hRemovedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_REMOVEDLINE
),
106 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
107 m_hAddedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ADDEDLINE
),
108 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
109 m_hWhitespaceBlockIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WHITESPACELINE
),
110 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
111 m_hEqualIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EQUALLINE
),
112 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
113 m_hLineEndingCR
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCR
),
114 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
115 m_hLineEndingCRLF
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCRLF
),
116 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
117 m_hLineEndingLF
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGLF
),
118 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
119 m_hEditedIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEEDITED
),
120 IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
121 for (int i
=0; i
<1024; ++i
)
122 m_sConflictedText
+= _T("??");
123 m_sNoLineNr
.LoadString(IDS_EMPTYLINETT
);
127 CBaseView::~CBaseView()
131 m_pCacheBitmap
->DeleteObject();
132 delete m_pCacheBitmap
;
134 for (int i
=0; i
<MAXFONTS
; i
++)
136 if (m_apFonts
[i
] != NULL
)
138 m_apFonts
[i
]->DeleteObject();
143 DestroyIcon(m_hAddedIcon
);
144 DestroyIcon(m_hRemovedIcon
);
145 DestroyIcon(m_hConflictedIcon
);
146 DestroyIcon(m_hConflictedIgnoredIcon
);
147 DestroyIcon(m_hWhitespaceBlockIcon
);
148 DestroyIcon(m_hEqualIcon
);
149 DestroyIcon(m_hLineEndingCR
);
150 DestroyIcon(m_hLineEndingCRLF
);
151 DestroyIcon(m_hLineEndingLF
);
152 DestroyIcon(m_hEditedIcon
);
155 BEGIN_MESSAGE_MAP(CBaseView
, CView
)
167 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE
, OnMergeNextdifference
)
168 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE
, OnMergePreviousdifference
)
169 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, OnToolTipNotify
)
170 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, OnToolTipNotify
)
173 ON_COMMAND(ID_EDIT_COPY
, OnEditCopy
)
175 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT
, OnMergePreviousconflict
)
176 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT
, OnMergeNextconflict
)
178 ON_COMMAND(ID_CARET_DOWN
, &CBaseView::OnCaretDown
)
179 ON_COMMAND(ID_CARET_LEFT
, &CBaseView::OnCaretLeft
)
180 ON_COMMAND(ID_CARET_RIGHT
, &CBaseView::OnCaretRight
)
181 ON_COMMAND(ID_CARET_UP
, &CBaseView::OnCaretUp
)
182 ON_COMMAND(ID_CARET_WORDLEFT
, &CBaseView::OnCaretWordleft
)
183 ON_COMMAND(ID_CARET_WORDRIGHT
, &CBaseView::OnCaretWordright
)
184 ON_COMMAND(ID_EDIT_CUT
, &CBaseView::OnEditCut
)
185 ON_COMMAND(ID_EDIT_PASTE
, &CBaseView::OnEditPaste
)
191 void CBaseView::DocumentUpdated()
193 if (m_pCacheBitmap
!= NULL
)
195 m_pCacheBitmap
->DeleteObject();
196 delete m_pCacheBitmap
;
197 m_pCacheBitmap
= NULL
;
202 m_nMaxLineLength
= -1;
206 m_bOtherDiffChecked
= false;
210 m_nTabSize
= (int)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
211 m_bViewLinenumbers
= CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
212 m_bShowInlineDiff
= CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE
);
213 m_InlineAddedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR
);
214 m_InlineRemovedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR
);
215 m_ModifiedBk
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR
);
216 m_WhiteSpaceFg
= CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT
));
217 m_bIconLFs
= CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
218 for (int i
=0; i
<MAXFONTS
; i
++)
220 if (m_apFonts
[i
] != NULL
)
222 m_apFonts
[i
]->DeleteObject();
227 m_nSelBlockStart
= -1;
229 RecalcVertScrollBar();
230 RecalcHorzScrollBar();
235 void CBaseView::UpdateStatusBar()
237 int nRemovedLines
= 0;
239 int nConflictedLines
= 0;
243 for (int i
=0; i
<m_pViewData
->GetCount(); i
++)
245 DiffStates state
= m_pViewData
->GetState(i
);
248 case DIFFSTATE_ADDED
:
249 case DIFFSTATE_IDENTICALADDED
:
250 case DIFFSTATE_THEIRSADDED
:
251 case DIFFSTATE_YOURSADDED
:
252 case DIFFSTATE_CONFLICTADDED
:
255 case DIFFSTATE_IDENTICALREMOVED
:
256 case DIFFSTATE_REMOVED
:
257 case DIFFSTATE_THEIRSREMOVED
:
258 case DIFFSTATE_YOURSREMOVED
:
261 case DIFFSTATE_CONFLICTED
:
262 case DIFFSTATE_CONFLICTED_IGNORED
:
274 case CFileTextLines::ASCII
:
275 sBarText
= _T("ASCII ");
277 case CFileTextLines::BINARY
:
278 sBarText
= _T("BINARY ");
280 case CFileTextLines::UNICODE_LE
:
281 sBarText
= _T("UTF-16LE ");
283 case CFileTextLines::UTF8
:
284 sBarText
= _T("UTF8 ");
286 case CFileTextLines::UTF8BOM
:
287 sBarText
= _T("UTF8 BOM ");
294 sBarText
+= _T("LF ");
297 sBarText
+= _T("CRLF ");
300 sBarText
+= _T("LFCR ");
303 sBarText
+= _T("CR ");
307 if (sBarText
.IsEmpty())
308 sBarText
+= _T(" / ");
312 sTemp
.Format(IDS_STATUSBAR_REMOVEDLINES
, nRemovedLines
);
313 if (!sBarText
.IsEmpty())
314 sBarText
+= _T(" / ");
319 sTemp
.Format(IDS_STATUSBAR_ADDEDLINES
, nAddedLines
);
320 if (!sBarText
.IsEmpty())
321 sBarText
+= _T(" / ");
324 if (nConflictedLines
)
326 sTemp
.Format(IDS_STATUSBAR_CONFLICTEDLINES
, nConflictedLines
);
327 if (!sBarText
.IsEmpty())
328 sBarText
+= _T(" / ");
336 int nIndex
= m_pwndStatusBar
->CommandToIndex(m_nStatusBarID
);
337 if (m_nStatusBarID
== ID_INDICATOR_BOTTOMVIEW
)
339 sBarText
.Format(IDS_STATUSBAR_CONFLICTS
, nConflictedLines
);
341 if (m_nStatusBarID
== ID_INDICATOR_LEFTVIEW
)
343 sTemp
.LoadString(IDS_STATUSBAR_LEFTVIEW
);
344 sBarText
= sTemp
+sBarText
;
346 if (m_nStatusBarID
== ID_INDICATOR_RIGHTVIEW
)
348 sTemp
.LoadString(IDS_STATUSBAR_RIGHTVIEW
);
349 sBarText
= sTemp
+sBarText
;
351 m_pwndStatusBar
->GetPaneInfo(nIndex
, nID
, nStyle
, cxWidth
);
352 //calculate the width of the text
353 CDC
* pDC
= m_pwndStatusBar
->GetDC();
356 CSize size
= pDC
->GetTextExtent(sBarText
);
357 m_pwndStatusBar
->SetPaneInfo(nIndex
, nID
, nStyle
, size
.cx
+2);
360 m_pwndStatusBar
->SetPaneText(nIndex
, sBarText
);
364 BOOL
CBaseView::PreCreateWindow(CREATESTRUCT
& cs
)
366 if (!CView::PreCreateWindow(cs
))
369 cs
.dwExStyle
|= WS_EX_CLIENTEDGE
;
370 cs
.style
&= ~WS_BORDER
;
371 cs
.lpszClass
= AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW
|CS_DBLCLKS
,
372 ::LoadCursor(NULL
, IDC_ARROW
), reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1), NULL
);
374 CWnd
*pParentWnd
= CWnd::FromHandlePermanent(cs
.hwndParent
);
375 if (pParentWnd
== NULL
|| ! pParentWnd
->IsKindOf(RUNTIME_CLASS(CSplitterWnd
)))
377 // View must always create its own scrollbars,
378 // if only it's not used within splitter
379 cs
.style
|= (WS_HSCROLL
| WS_VSCROLL
);
381 cs
.lpszClass
= AfxRegisterWndClass(CS_DBLCLKS
);
385 CFont
* CBaseView::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/, BOOL bStrikeOut
/*= FALSE*/)
394 if (m_apFonts
[nIndex
] == NULL
)
396 m_apFonts
[nIndex
] = new CFont
;
397 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
398 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
399 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
400 m_lfBaseFont
.lfStrikeOut
= (BYTE
) bStrikeOut
;
402 m_lfBaseFont
.lfStrikeOut
= (BYTE
)(DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE
);
406 m_lfBaseFont
.lfHeight
= -MulDiv((DWORD
)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC
->m_hDC
, LOGPIXELSY
), 72);
409 _tcsncpy_s(m_lfBaseFont
.lfFaceName
, 32, (LPCTSTR
)(CString
)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
410 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
412 delete m_apFonts
[nIndex
];
413 m_apFonts
[nIndex
] = NULL
;
414 return CView::GetFont();
417 return m_apFonts
[nIndex
];
420 void CBaseView::CalcLineCharDim()
423 CFont
*pOldFont
= pDC
->SelectObject(GetFont());
424 CSize szCharExt
= pDC
->GetTextExtent(_T("X"));
425 m_nLineHeight
= szCharExt
.cy
;
426 if (m_nLineHeight
<= 0)
428 m_nCharWidth
= szCharExt
.cx
;
429 if (m_nCharWidth
<= 0)
431 pDC
->SelectObject(pOldFont
);
435 int CBaseView::GetScreenChars()
437 if (m_nScreenChars
== -1)
440 GetClientRect(&rect
);
441 m_nScreenChars
= (rect
.Width() - GetMarginWidth()) / GetCharWidth();
443 return m_nScreenChars
;
446 int CBaseView::GetAllMinScreenChars() const
449 if (IsLeftViewGood())
450 nChars
= m_pwndLeft
->GetScreenChars();
451 if (IsRightViewGood())
452 nChars
= (nChars
< m_pwndRight
->GetScreenChars() ? nChars
: m_pwndRight
->GetScreenChars());
453 if (IsBottomViewGood())
454 nChars
= (nChars
< m_pwndBottom
->GetScreenChars() ? nChars
: m_pwndBottom
->GetScreenChars());
458 int CBaseView::GetAllMaxLineLength() const
461 if (IsLeftViewGood())
462 nLength
= m_pwndLeft
->GetMaxLineLength();
463 if (IsRightViewGood())
464 nLength
= (nLength
> m_pwndRight
->GetMaxLineLength() ? nLength
: m_pwndRight
->GetMaxLineLength());
465 if (IsBottomViewGood())
466 nLength
= (nLength
> m_pwndBottom
->GetMaxLineLength() ? nLength
: m_pwndBottom
->GetMaxLineLength());
470 int CBaseView::GetLineHeight()
472 if (m_nLineHeight
== -1)
474 if (m_nLineHeight
<= 0)
476 return m_nLineHeight
;
479 int CBaseView::GetCharWidth()
481 if (m_nCharWidth
== -1)
483 if (m_nCharWidth
<= 0)
488 int CBaseView::GetMaxLineLength()
490 if (m_nMaxLineLength
== -1)
492 m_nMaxLineLength
= 0;
493 int nLineCount
= GetLineCount();
494 for (int i
=0; i
<nLineCount
; i
++)
496 int nActualLength
= GetLineActualLength(i
);
497 if (m_nMaxLineLength
< nActualLength
)
498 m_nMaxLineLength
= nActualLength
;
501 return m_nMaxLineLength
;
504 int CBaseView::GetLineActualLength(int index
) const
506 if (m_pViewData
== NULL
)
509 return CalculateActualOffset(index
, GetLineLength(index
));
512 int CBaseView::GetLineLength(int index
) const
514 if (m_pViewData
== NULL
)
516 if (m_pViewData
->GetCount() == 0)
518 int nLineLength
= m_pViewData
->GetLine(index
).GetLength();
519 ASSERT(nLineLength
>= 0);
523 int CBaseView::GetLineCount() const
525 if (m_pViewData
== NULL
)
527 int nLineCount
= m_pViewData
->GetCount();
528 ASSERT(nLineCount
>= 0);
532 LPCTSTR
CBaseView::GetLineChars(int index
) const
534 if (m_pViewData
== NULL
)
536 if (m_pViewData
->GetCount() == 0)
538 return m_pViewData
->GetLine(index
);
541 void CBaseView::CheckOtherView()
543 if (m_bOtherDiffChecked
)
545 // find out what the 'other' file is
546 m_pOtherViewData
= NULL
;
547 if (this == m_pwndLeft
&& IsRightViewGood())
548 m_pOtherViewData
= m_pwndRight
->m_pViewData
;
550 if (this == m_pwndRight
&& IsLeftViewGood())
551 m_pOtherViewData
= m_pwndLeft
->m_pViewData
;
553 m_bOtherDiffChecked
= true;
556 CString
CBaseView::GetWhitespaceBlock(CViewData
*viewData
, int nLineIndex
)
558 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
561 DiffStates origstate
= viewData
->GetState(nLineIndex
);
563 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
564 int nStartBlock
= nLineIndex
;
565 int nEndBlock
= nLineIndex
;
566 while ((nStartBlock
> 0) && (nStartBlock
> (nLineIndex
- MAX_WHITESPACEBLOCK_SIZE
)))
568 DiffStates state
= viewData
->GetState(nStartBlock
- 1);
569 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
571 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
576 while ((nEndBlock
< (viewData
->GetCount() - 1)) && (nEndBlock
< (nLineIndex
+ MAX_WHITESPACEBLOCK_SIZE
)))
578 DiffStates state
= viewData
->GetState(nEndBlock
+ 1);
579 if ((origstate
== DIFFSTATE_EMPTY
) && (state
!= DIFFSTATE_NORMAL
))
581 if ((origstate
== state
) || (state
== DIFFSTATE_EMPTY
))
588 for (int i
= nStartBlock
; i
<= nEndBlock
; ++i
)
589 block
+= viewData
->GetLine(i
);
593 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex
, bool& bIdentical
)
595 enum { MAX_WHITESPACEBLOCK_SIZE
= 8 };
597 if (!m_pOtherViewData
)
600 (m_pViewData
->GetState(nLineIndex
) == DIFFSTATE_NORMAL
) &&
601 (m_pOtherViewData
->GetLine(nLineIndex
) == m_pViewData
->GetLine(nLineIndex
))
605 CString mine
= GetWhitespaceBlock(m_pViewData
, nLineIndex
);
606 CString other
= GetWhitespaceBlock(m_pOtherViewData
, min(nLineIndex
, m_pOtherViewData
->GetCount() - 1));
607 bIdentical
= mine
== other
;
609 mine
.Replace(_T(" "), _T(""));
610 mine
.Replace(_T("\t"), _T(""));
611 mine
.Replace(_T("\r"), _T(""));
612 mine
.Replace(_T("\n"), _T(""));
613 other
.Replace(_T(" "), _T(""));
614 other
.Replace(_T("\t"), _T(""));
615 other
.Replace(_T("\r"), _T(""));
616 other
.Replace(_T("\n"), _T(""));
618 return (mine
== other
) && (!mine
.IsEmpty());
621 int CBaseView::GetLineNumber(int index
) const
623 if (m_pViewData
== NULL
)
625 if (m_pViewData
->GetLineNumber(index
)==DIFF_EMPTYLINENUMBER
)
627 return m_pViewData
->GetLineNumber(index
);
630 int CBaseView::GetScreenLines()
632 if (m_nScreenLines
== -1)
635 sbi
.cbSize
= sizeof(sbi
);
636 GetScrollBarInfo(OBJID_HSCROLL
, &sbi
);
637 int scrollBarHeight
= sbi
.rcScrollBar
.bottom
- sbi
.rcScrollBar
.top
;
640 GetClientRect(&rect
);
641 m_nScreenLines
= (rect
.Height() - HEADERHEIGHT
- scrollBarHeight
) / GetLineHeight();
643 return m_nScreenLines
;
646 int CBaseView::GetAllMinScreenLines() const
649 if (IsLeftViewGood())
650 nLines
= m_pwndLeft
->GetScreenLines();
651 if (IsRightViewGood())
652 nLines
= (nLines
< m_pwndRight
->GetScreenLines() ? nLines
: m_pwndRight
->GetScreenLines());
653 if (IsBottomViewGood())
654 nLines
= (nLines
< m_pwndBottom
->GetScreenLines() ? nLines
: m_pwndBottom
->GetScreenLines());
658 int CBaseView::GetAllLineCount() const
661 if (IsLeftViewGood())
662 nLines
= m_pwndLeft
->GetLineCount();
663 if (IsRightViewGood())
664 nLines
= (nLines
> m_pwndRight
->GetLineCount() ? nLines
: m_pwndRight
->GetLineCount());
665 if (IsBottomViewGood())
666 nLines
= (nLines
> m_pwndBottom
->GetLineCount() ? nLines
: m_pwndBottom
->GetLineCount());
670 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly
/*= FALSE*/)
672 if (IsLeftViewGood())
673 m_pwndLeft
->RecalcVertScrollBar(bPositionOnly
);
674 if (IsRightViewGood())
675 m_pwndRight
->RecalcVertScrollBar(bPositionOnly
);
676 if (IsBottomViewGood())
677 m_pwndBottom
->RecalcVertScrollBar(bPositionOnly
);
680 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly
/*= FALSE*/)
683 si
.cbSize
= sizeof(si
);
687 si
.nPos
= m_nTopLine
;
691 EnableScrollBarCtrl(SB_VERT
, TRUE
);
692 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine
> 0)
697 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
699 si
.nMax
= GetAllLineCount();
700 si
.nPage
= GetAllMinScreenLines();
701 si
.nPos
= m_nTopLine
;
703 VERIFY(SetScrollInfo(SB_VERT
, &si
));
706 void CBaseView::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
708 CView::OnVScroll(nSBCode
, nPos
, pScrollBar
);
710 m_pwndLeft
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
712 m_pwndRight
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
714 m_pwndBottom
->OnDoVScroll(nSBCode
, nPos
, pScrollBar
, this);
716 m_pwndLocator
->Invalidate();
719 void CBaseView::OnDoVScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
721 // Note we cannot use nPos because of its 16-bit nature
723 si
.cbSize
= sizeof(si
);
725 VERIFY(master
->GetScrollInfo(SB_VERT
, &si
));
727 int nPageLines
= GetScreenLines();
728 int nLineCount
= GetLineCount();
734 static LONG textwidth
= 0;
735 static CString
sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT
));
742 nNewTopLine
= nLineCount
- nPageLines
+ 1;
745 nNewTopLine
= m_nTopLine
- 1;
748 nNewTopLine
= m_nTopLine
+ 1;
751 nNewTopLine
= m_nTopLine
- si
.nPage
+ 1;
754 nNewTopLine
= m_nTopLine
+ si
.nPage
- 1;
756 case SB_THUMBPOSITION
:
757 m_ScrollTool
.Clear();
758 nNewTopLine
= si
.nTrackPos
;
762 nNewTopLine
= si
.nTrackPos
;
763 if (GetFocus() == this)
765 GetClientRect(&thumbrect
);
766 ClientToScreen(&thumbrect
);
767 thumbpoint
.x
= thumbrect
.right
;
768 thumbpoint
.y
= thumbrect
.top
+ ((thumbrect
.bottom
-thumbrect
.top
)*si
.nTrackPos
)/(si
.nMax
-si
.nMin
);
769 m_ScrollTool
.Init(&thumbpoint
);
772 CString sTemp
= sFormat
;
773 sTemp
.Format(sFormat
, m_nDigits
, 10*m_nDigits
-1);
774 textwidth
= m_ScrollTool
.GetTextWidth(sTemp
);
776 thumbpoint
.x
-= textwidth
;
777 int line
= GetLineNumber(nNewTopLine
);
779 m_ScrollTool
.SetText(&thumbpoint
, sFormat
, m_nDigits
, GetLineNumber(nNewTopLine
)+1);
781 m_ScrollTool
.SetText(&thumbpoint
, m_sNoLineNr
);
790 if (nNewTopLine
>= nLineCount
)
791 nNewTopLine
= nLineCount
- 1;
792 ScrollToLine(nNewTopLine
);
795 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly
/*= FALSE*/)
797 if (IsLeftViewGood())
798 m_pwndLeft
->RecalcHorzScrollBar(bPositionOnly
);
799 if (IsRightViewGood())
800 m_pwndRight
->RecalcHorzScrollBar(bPositionOnly
);
801 if (IsBottomViewGood())
802 m_pwndBottom
->RecalcHorzScrollBar(bPositionOnly
);
805 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly
/*= FALSE*/)
808 si
.cbSize
= sizeof(si
);
812 si
.nPos
= m_nOffsetChar
;
816 EnableScrollBarCtrl(SB_HORZ
, TRUE
);
817 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar
> 0)
822 si
.fMask
= SIF_DISABLENOSCROLL
| SIF_PAGE
| SIF_POS
| SIF_RANGE
;
824 si
.nMax
= GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
825 si
.nPage
= GetAllMinScreenChars();
826 si
.nPos
= m_nOffsetChar
;
828 VERIFY(SetScrollInfo(SB_HORZ
, &si
));
831 void CBaseView::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
833 CView::OnHScroll(nSBCode
, nPos
, pScrollBar
);
835 m_pwndLeft
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
837 m_pwndRight
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
839 m_pwndBottom
->OnDoHScroll(nSBCode
, nPos
, pScrollBar
, this);
841 m_pwndLocator
->Invalidate();
844 void CBaseView::OnDoHScroll(UINT nSBCode
, UINT
/*nPos*/, CScrollBar
* /*pScrollBar*/, CBaseView
* master
)
847 si
.cbSize
= sizeof(si
);
849 VERIFY(master
->GetScrollInfo(SB_HORZ
, &si
));
851 int nPageChars
= GetScreenChars();
852 int nMaxLineLength
= GetMaxLineLength();
861 nNewOffset
= nMaxLineLength
- nPageChars
+ 1;
864 nNewOffset
= m_nOffsetChar
- 1;
867 nNewOffset
= m_nOffsetChar
+ 1;
870 nNewOffset
= m_nOffsetChar
- si
.nPage
+ 1;
873 nNewOffset
= m_nOffsetChar
+ si
.nPage
- 1;
875 case SB_THUMBPOSITION
:
877 nNewOffset
= si
.nTrackPos
;
883 if (nNewOffset
>= nMaxLineLength
)
884 nNewOffset
= nMaxLineLength
- 1;
887 ScrollToChar(nNewOffset
, TRUE
);
890 void CBaseView::ScrollToChar(int nNewOffsetChar
, BOOL bTrackScrollBar
/*= TRUE*/)
892 if (m_nOffsetChar
!= nNewOffsetChar
)
894 int nScrollChars
= m_nOffsetChar
- nNewOffsetChar
;
895 m_nOffsetChar
= nNewOffsetChar
;
897 GetClientRect(&rcScroll
);
898 rcScroll
.left
+= GetMarginWidth();
899 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
900 ScrollWindow(nScrollChars
* GetCharWidth(), 0, &rcScroll
, &rcScroll
);
901 // update the view header
904 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
905 InvalidateRect(&rcScroll
, FALSE
);
908 RecalcHorzScrollBar(TRUE
);
913 void CBaseView::ScrollSide(int delta
)
915 int nNewOffset
= m_nOffsetChar
;
917 int nMaxLineLength
= GetMaxLineLength();
918 if (nNewOffset
>= nMaxLineLength
)
919 nNewOffset
= nMaxLineLength
- 1;
922 ScrollToChar(nNewOffset
, TRUE
);
923 if (m_pwndLineDiffBar
)
924 m_pwndLineDiffBar
->Invalidate();
928 void CBaseView::ScrollToLine(int nNewTopLine
, BOOL bTrackScrollBar
/*= TRUE*/)
930 if (m_nTopLine
!= nNewTopLine
)
934 int nScrollLines
= m_nTopLine
- nNewTopLine
;
935 m_nTopLine
= nNewTopLine
;
937 GetClientRect(&rcScroll
);
938 rcScroll
.top
+= GetLineHeight()+HEADERHEIGHT
;
939 ScrollWindow(0, nScrollLines
* GetLineHeight(), &rcScroll
, &rcScroll
);
942 RecalcVertScrollBar(TRUE
);
948 void CBaseView::DrawMargin(CDC
*pdc
, const CRect
&rect
, int nLineIndex
)
950 pdc
->FillSolidRect(rect
, ::GetSysColor(COLOR_SCROLLBAR
));
952 if ((nLineIndex
>= 0)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
954 DiffStates state
= m_pViewData
->GetState(nLineIndex
);
958 case DIFFSTATE_ADDED
:
959 case DIFFSTATE_THEIRSADDED
:
960 case DIFFSTATE_YOURSADDED
:
961 case DIFFSTATE_IDENTICALADDED
:
962 case DIFFSTATE_CONFLICTADDED
:
965 case DIFFSTATE_REMOVED
:
966 case DIFFSTATE_THEIRSREMOVED
:
967 case DIFFSTATE_YOURSREMOVED
:
968 case DIFFSTATE_IDENTICALREMOVED
:
969 icon
= m_hRemovedIcon
;
971 case DIFFSTATE_CONFLICTED
:
972 icon
= m_hConflictedIcon
;
974 case DIFFSTATE_CONFLICTED_IGNORED
:
975 icon
= m_hConflictedIgnoredIcon
;
977 case DIFFSTATE_EDITED
:
978 icon
= m_hEditedIcon
;
983 bool bIdentical
= false;
984 if ((state
!= DIFFSTATE_EDITED
)&&(IsBlockWhitespaceOnly(nLineIndex
, bIdentical
)))
989 icon
= m_hWhitespaceBlockIcon
;
994 ::DrawIconEx(pdc
->m_hDC
, rect
.left
+ 2, rect
.top
+ (rect
.Height()-16)/2, icon
, 16, 16, NULL
, NULL
, DI_NORMAL
);
996 if ((m_bViewLinenumbers
)&&(m_nDigits
))
998 int nLineNumber
= GetLineNumber(nLineIndex
);
999 if (nLineNumber
>= 0)
1001 CString sLinenumberFormat
;
1002 CString sLinenumber
;
1003 sLinenumberFormat
.Format(_T("%%%dd"), m_nDigits
);
1004 sLinenumber
.Format(sLinenumberFormat
, nLineNumber
+1);
1005 pdc
->SetBkColor(::GetSysColor(COLOR_SCROLLBAR
));
1006 pdc
->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT
));
1008 pdc
->SelectObject(GetFont());
1009 pdc
->ExtTextOut(rect
.left
+ 18, rect
.top
, ETO_CLIPPED
, &rect
, sLinenumber
, NULL
);
1015 int CBaseView::GetMarginWidth()
1017 if ((m_bViewLinenumbers
)&&(m_pViewData
)&&(m_pViewData
->GetCount()))
1019 int nWidth
= GetCharWidth();
1022 int nLength
= (int)m_pViewData
->GetCount();
1023 // find out how many digits are needed to show the highest line number
1025 while (nLength
/ 10)
1030 m_nDigits
= nDigits
;
1032 return (MARGINWIDTH
+ (m_nDigits
* nWidth
) + 2);
1037 void CBaseView::DrawHeader(CDC
*pdc
, const CRect
&rect
)
1039 CRect
textrect(rect
.left
, rect
.top
, rect
.Width(), GetLineHeight()+HEADERHEIGHT
);
1040 COLORREF crBk
, crFg
;
1041 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL
, crBk
, crFg
);
1042 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
1043 if (IsBottomViewGood())
1045 pdc
->SetBkColor(crBk
);
1050 if (this == m_pwndRight
)
1052 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED
, crBk
, crFg
);
1053 pdc
->SetBkColor(crBk
);
1057 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED
, crBk
, crFg
);
1058 pdc
->SetBkColor(crBk
);
1061 pdc
->FillSolidRect(textrect
, crBk
);
1063 pdc
->SetTextColor(crFg
);
1065 pdc
->SelectObject(GetFont(FALSE
, TRUE
, FALSE
));
1068 if (m_sWindowName
.Left(2).Compare(_T("* "))!=0)
1069 m_sWindowName
= _T("* ") + m_sWindowName
;
1073 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
1074 m_sWindowName
= m_sWindowName
.Mid(2);
1076 CString sViewTitle
= m_sWindowName
;
1077 int nStringLength
= (GetCharWidth()*m_sWindowName
.GetLength());
1078 if (nStringLength
> rect
.Width())
1080 int offset
= min(m_nOffsetChar
, (nStringLength
-rect
.Width())/GetCharWidth()+1);
1082 sViewTitle
= m_sWindowName
.Mid(offset
);
1084 pdc
->ExtTextOut(max(rect
.left
+ (rect
.Width()-nStringLength
)/2, 1),
1085 rect
.top
+(HEADERHEIGHT
/2), ETO_CLIPPED
, textrect
, sViewTitle
, NULL
);
1086 if (this->GetFocus() == this)
1087 pdc
->DrawEdge(textrect
, EDGE_BUMP
, BF_RECT
);
1089 pdc
->DrawEdge(textrect
, EDGE_ETCHED
, BF_RECT
);
1092 void CBaseView::OnDraw(CDC
* pDC
)
1095 GetClientRect(rcClient
);
1097 int nLineCount
= GetLineCount();
1098 int nLineHeight
= GetLineHeight();
1101 VERIFY(cacheDC
.CreateCompatibleDC(pDC
));
1102 if (m_pCacheBitmap
== NULL
)
1104 m_pCacheBitmap
= new CBitmap
;
1105 VERIFY(m_pCacheBitmap
->CreateCompatibleBitmap(pDC
, rcClient
.Width(), nLineHeight
));
1107 CBitmap
*pOldBitmap
= cacheDC
.SelectObject(m_pCacheBitmap
);
1109 DrawHeader(pDC
, rcClient
);
1113 rcLine
.top
+= nLineHeight
+HEADERHEIGHT
;
1114 rcLine
.bottom
= rcLine
.top
+ nLineHeight
;
1115 CRect
rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight
);
1116 CRect
rcCacheLine(GetMarginWidth(), 0, rcLine
.Width(), nLineHeight
);
1118 int nCurrentLine
= m_nTopLine
;
1119 while (rcLine
.top
< rcClient
.bottom
)
1121 if (nCurrentLine
< nLineCount
)
1123 DrawMargin(&cacheDC
, rcCacheMargin
, nCurrentLine
);
1124 DrawSingleLine(&cacheDC
, rcCacheLine
, nCurrentLine
);
1128 DrawMargin(&cacheDC
, rcCacheMargin
, -1);
1129 DrawSingleLine(&cacheDC
, rcCacheLine
, -1);
1132 VERIFY(pDC
->BitBlt(rcLine
.left
, rcLine
.top
, rcLine
.Width(), rcLine
.Height(), &cacheDC
, 0, 0, SRCCOPY
));
1135 rcLine
.OffsetRect(0, nLineHeight
);
1138 cacheDC
.SelectObject(pOldBitmap
);
1142 BOOL
CBaseView::IsLineRemoved(int nLineIndex
)
1144 DiffStates state
= DIFFSTATE_UNKNOWN
;
1146 state
= m_pViewData
->GetState(nLineIndex
);
1150 case DIFFSTATE_REMOVED
:
1151 case DIFFSTATE_THEIRSREMOVED
:
1152 case DIFFSTATE_YOURSREMOVED
:
1153 case DIFFSTATE_IDENTICALREMOVED
:
1163 bool CBaseView::IsLineConflicted(int nLineIndex
)
1165 DiffStates state
= DIFFSTATE_UNKNOWN
;
1167 state
= m_pViewData
->GetState(nLineIndex
);
1171 case DIFFSTATE_CONFLICTED
:
1172 case DIFFSTATE_CONFLICTED_IGNORED
:
1173 case DIFFSTATE_CONFLICTEMPTY
:
1174 case DIFFSTATE_CONFLICTADDED
:
1184 COLORREF
CBaseView::IntenseColor(long scale
, COLORREF col
)
1186 // if the color is already dark (gray scale below 127),
1187 // then lighten the color by 'scale', otherwise darken it
1188 int Gray
= (((int)GetRValue(col
)) + GetGValue(col
) + GetBValue(col
))/3;
1191 long red
= MulDiv(GetRValue(col
),(255-scale
),255);
1192 long green
= MulDiv(GetGValue(col
),(255-scale
),255);
1193 long blue
= MulDiv(GetBValue(col
),(255-scale
),255);
1195 return RGB(red
, green
, blue
);
1197 long R
= MulDiv(255-GetRValue(col
),scale
,255)+GetRValue(col
);
1198 long G
= MulDiv(255-GetGValue(col
),scale
,255)+GetGValue(col
);
1199 long B
= MulDiv(255-GetBValue(col
),scale
,255)+GetBValue(col
);
1201 return RGB(R
, G
, B
);
1204 COLORREF
CBaseView::InlineDiffColor(int nLineIndex
)
1206 return IsLineRemoved(nLineIndex
) ? m_InlineRemovedBk
: m_InlineAddedBk
;
1209 void CBaseView::DrawLineEnding(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CPoint
& origin
)
1211 if (!(m_bViewWhitespace
&& m_pViewData
&& (nLineIndex
>= 0) && (nLineIndex
< m_pViewData
->GetCount())))
1214 EOL ending
= m_pViewData
->GetLineEnding(nLineIndex
);
1217 HICON hEndingIcon
= NULL
;
1220 case EOL_CR
: hEndingIcon
= m_hLineEndingCR
; break;
1221 case EOL_CRLF
: hEndingIcon
= m_hLineEndingCRLF
; break;
1222 case EOL_LF
: hEndingIcon
= m_hLineEndingLF
; break;
1225 if (origin
.x
< (rc
.left
-GetCharWidth()))
1227 // If EOL style has changed, color end-of-line markers as inline differences.
1229 m_bShowInlineDiff
&& m_pOtherViewData
&&
1230 (nLineIndex
< m_pOtherViewData
->GetCount()) &&
1231 (ending
!= EOL_NOENDING
) &&
1232 (ending
!= m_pOtherViewData
->GetLineEnding(nLineIndex
) &&
1233 (m_pOtherViewData
->GetLineEnding(nLineIndex
) != EOL_NOENDING
))
1236 pDC
->FillSolidRect(origin
.x
, origin
.y
, rc
.Height(), rc
.Height(), InlineDiffColor(nLineIndex
));
1239 DrawIconEx(pDC
->GetSafeHdc(), origin
.x
, origin
.y
, hEndingIcon
, rc
.Height(), rc
.Height(), NULL
, NULL
, DI_NORMAL
);
1243 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1244 CPen
* oldpen
= pDC
->SelectObject(&pen
);
1245 int yMiddle
= origin
.y
+ rc
.Height()/2;
1246 int xMiddle
= origin
.x
+GetCharWidth()/2;
1250 // arrow from right to left
1251 pDC
->MoveTo(origin
.x
+GetCharWidth(), yMiddle
);
1252 pDC
->LineTo(origin
.x
, yMiddle
);
1253 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1254 pDC
->MoveTo(origin
.x
, yMiddle
);
1255 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1258 // arrow from top to middle+2, then left
1259 pDC
->MoveTo(origin
.x
+GetCharWidth(), rc
.top
);
1260 pDC
->LineTo(origin
.x
+GetCharWidth(), yMiddle
);
1261 pDC
->LineTo(origin
.x
, yMiddle
);
1262 pDC
->LineTo(origin
.x
+4, yMiddle
+4);
1263 pDC
->MoveTo(origin
.x
, yMiddle
);
1264 pDC
->LineTo(origin
.x
+4, yMiddle
-4);
1267 // arrow from top to bottom
1268 pDC
->MoveTo(xMiddle
, rc
.top
);
1269 pDC
->LineTo(xMiddle
, rc
.bottom
-1);
1270 pDC
->LineTo(xMiddle
+4, rc
.bottom
-5);
1271 pDC
->MoveTo(xMiddle
, rc
.bottom
-1);
1272 pDC
->LineTo(xMiddle
-4, rc
.bottom
-5);
1275 pDC
->SelectObject(oldpen
);
1279 void CBaseView::DrawBlockLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1281 const int THICKNESS
= 2;
1282 COLORREF rectcol
= GetSysColor(m_bFocused
? COLOR_WINDOWTEXT
: COLOR_GRAYTEXT
);
1283 if ((nLineIndex
== m_nSelBlockStart
) && m_bShowSelection
)
1285 pDC
->FillSolidRect(rc
.left
, rc
.top
, rc
.Width(), THICKNESS
, rectcol
);
1287 if ((nLineIndex
== m_nSelBlockEnd
) && m_bShowSelection
)
1289 pDC
->FillSolidRect(rc
.left
, rc
.bottom
- THICKNESS
, rc
.Width(), THICKNESS
, rectcol
);
1293 void CBaseView::DrawText(
1294 CDC
* pDC
, const CRect
&rc
, LPCTSTR text
, int textlength
, int nLineIndex
, POINT coords
, bool bModified
, bool bInlineDiff
)
1296 ASSERT(m_pViewData
&& (nLineIndex
< m_pViewData
->GetCount()));
1297 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1299 // first suppose the whole line is selected
1300 int selectedStart
= 0, selectedEnd
= textlength
;
1302 if ((m_ptSelectionStartPos
.y
> nLineIndex
) || (m_ptSelectionEndPos
.y
< nLineIndex
)
1303 || ! m_bShowSelection
)
1305 // this line has no selected text
1306 selectedStart
= textlength
;
1308 else if ((m_ptSelectionStartPos
.y
== nLineIndex
) || (m_ptSelectionEndPos
.y
== nLineIndex
))
1310 // the line is partially selected
1311 int xoffs
= m_nOffsetChar
+ (coords
.x
- GetMarginWidth()) / GetCharWidth();
1312 if (m_ptSelectionStartPos
.y
== nLineIndex
)
1314 // the first line of selection
1315 int nSelectionStartOffset
= CalculateActualOffset(m_ptSelectionStartPos
.y
, m_ptSelectionStartPos
.x
);
1316 selectedStart
= max(min(nSelectionStartOffset
- xoffs
, textlength
), 0);
1319 if (m_ptSelectionEndPos
.y
== nLineIndex
)
1321 // the last line of selection
1322 int nSelectionEndOffset
= CalculateActualOffset(m_ptSelectionEndPos
.y
, m_ptSelectionEndPos
.x
);
1323 selectedEnd
= max(min(nSelectionEndOffset
- xoffs
, textlength
), 0);
1327 COLORREF crBkgnd
, crText
;
1328 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1329 if (bModified
|| (diffState
== DIFFSTATE_EDITED
))
1330 crBkgnd
= m_ModifiedBk
;
1332 crBkgnd
= InlineDiffColor(nLineIndex
);
1334 pDC
->SetBkColor(crBkgnd
);
1335 pDC
->SetTextColor(crText
);
1336 if (selectedStart
>=0)
1337 VERIFY(pDC
->ExtTextOut(coords
.x
, coords
.y
, ETO_CLIPPED
, &rc
, text
, selectedStart
, NULL
));
1339 long intenseColorScale
= m_bFocused
? 70 : 30;
1340 pDC
->SetBkColor(IntenseColor(intenseColorScale
, crBkgnd
));
1341 pDC
->SetTextColor(IntenseColor(intenseColorScale
, crText
));
1342 VERIFY(pDC
->ExtTextOut(
1343 coords
.x
+ selectedStart
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1344 text
+ selectedStart
, selectedEnd
- selectedStart
, NULL
));
1346 pDC
->SetBkColor(crBkgnd
);
1347 pDC
->SetTextColor(crText
);
1348 if (textlength
- selectedEnd
>= 0)
1349 VERIFY(pDC
->ExtTextOut(
1350 coords
.x
+ selectedEnd
* GetCharWidth(), coords
.y
, ETO_CLIPPED
, &rc
,
1351 text
+ selectedEnd
, textlength
- selectedEnd
, NULL
));
1354 bool CBaseView::DrawInlineDiff(CDC
*pDC
, const CRect
&rc
, int nLineIndex
, const CString
&line
, CPoint
&origin
)
1356 if (!m_bShowInlineDiff
|| line
.IsEmpty())
1358 if ((m_pwndBottom
!= NULL
) && !(m_pwndBottom
->IsHidden()))
1361 LPCTSTR pszDiffChars
= NULL
;
1362 int nDiffLength
= 0;
1363 if (m_pOtherViewData
)
1365 int index
= min(nLineIndex
, m_pOtherViewData
->GetCount() - 1);
1366 pszDiffChars
= m_pOtherViewData
->GetLine(index
);
1367 nDiffLength
= m_pOtherViewData
->GetLine(index
).GetLength();
1370 if (!pszDiffChars
|| !*pszDiffChars
)
1374 ExpandChars(pszDiffChars
, 0, nDiffLength
, diffline
);
1375 svn_diff_t
* diff
= NULL
;
1376 m_svnlinediff
.Diff(&diff
, line
, line
.GetLength(), diffline
, diffline
.GetLength(), m_bInlineWordDiff
);
1377 if (!diff
|| !SVNLineDiff::ShowInlineDiff(diff
))
1381 std::deque
<int> removedPositions
;
1384 apr_off_t len
= diff
->original_length
;
1387 for (int i
= 0; i
< len
; ++i
)
1389 s
+= m_svnlinediff
.m_line1tokens
[lineoffset
].c_str();
1392 bool isModified
= diff
->type
== svn_diff__type_diff_modified
;
1393 DrawText(pDC
, rc
, (LPCTSTR
)s
, s
.GetLength(), nLineIndex
, origin
, true, isModified
);
1394 origin
.x
+= pDC
->GetTextExtent(s
).cx
;
1396 if (isModified
&& (len
< diff
->modified_length
))
1397 removedPositions
.push_back(origin
.x
- 1);
1401 // Draw vertical bars at removed chunks' positions.
1402 for (std::deque
<int>::iterator it
= removedPositions
.begin(); it
!= removedPositions
.end(); ++it
)
1403 pDC
->FillSolidRect(*it
, rc
.top
, 1, rc
.Height(), m_InlineRemovedBk
);
1407 void CBaseView::DrawSingleLine(CDC
*pDC
, const CRect
&rc
, int nLineIndex
)
1409 if (nLineIndex
>= GetLineCount())
1411 ASSERT(nLineIndex
>= -1);
1413 if ((nLineIndex
== -1) || !m_pViewData
)
1415 // Draw line beyond the text
1416 COLORREF crBkgnd
, crText
;
1417 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1418 pDC
->FillSolidRect(rc
, crBkgnd
);
1422 DiffStates diffState
= m_pViewData
->GetState(nLineIndex
);
1423 COLORREF crBkgnd
, crText
;
1424 CDiffColors::GetInstance().GetColors(diffState
, crBkgnd
, crText
);
1426 if (diffState
== DIFFSTATE_CONFLICTED
)
1428 // conflicted lines are shown without 'text' on them
1430 pDC
->FillSolidRect(rc
, crBkgnd
);
1431 // now draw some faint text patterns
1432 pDC
->SetTextColor(IntenseColor(130, crBkgnd
));
1433 pDC
->DrawText(m_sConflictedText
, rect
, DT_LEFT
|DT_NOPREFIX
|DT_SINGLELINE
);
1434 DrawBlockLine(pDC
, rc
, nLineIndex
);
1438 CPoint
origin(rc
.left
- m_nOffsetChar
* GetCharWidth(), rc
.top
);
1439 int nLength
= GetLineLength(nLineIndex
);
1442 // Draw the empty line
1443 pDC
->FillSolidRect(rc
, crBkgnd
);
1444 DrawBlockLine(pDC
, rc
, nLineIndex
);
1445 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1448 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
1449 if (pszChars
== NULL
)
1456 pDC
->SelectObject(GetFont(FALSE
, FALSE
, IsLineRemoved(nLineIndex
)));
1458 ExpandChars(pszChars
, 0, nLength
, line
);
1460 int nWidth
= rc
.right
- origin
.x
;
1461 int savedx
= origin
.x
;
1462 bool bInlineDiffDrawn
=
1463 nWidth
> 0 && diffState
!= DIFFSTATE_NORMAL
&&
1464 DrawInlineDiff(pDC
, rc
, nLineIndex
, line
, origin
);
1466 if (!bInlineDiffDrawn
)
1468 int nCount
= min(line
.GetLength(), nWidth
/ GetCharWidth() + 1);
1469 DrawText(pDC
, rc
, line
, nCount
, nLineIndex
, origin
, false, false);
1472 origin
.x
= savedx
+ pDC
->GetTextExtent(line
).cx
;
1474 // draw white space after the end of line
1476 if (origin
.x
> frect
.left
)
1477 frect
.left
= origin
.x
;
1478 if (bInlineDiffDrawn
)
1479 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN
, crBkgnd
, crText
);
1480 if (frect
.right
> frect
.left
)
1481 pDC
->FillSolidRect(frect
, crBkgnd
);
1482 // draw the whitespace chars
1483 if (m_bViewWhitespace
)
1486 int y
= rc
.top
+ (rc
.bottom
-rc
.top
)/2;
1488 int nActualOffset
= 0;
1489 while ((nActualOffset
< m_nOffsetChar
) && (*pszChars
))
1491 if (*pszChars
== _T('\t'))
1492 nActualOffset
+= (GetTabSize() - nActualOffset
% GetTabSize());
1497 if (nActualOffset
> m_nOffsetChar
)
1500 CPen
pen(PS_SOLID
, 0, m_WhiteSpaceFg
);
1501 CPen
pen2(PS_SOLID
, 2, m_WhiteSpaceFg
);
1509 CPen
* oldPen
= pDC
->SelectObject(&pen
);
1510 int nSpaces
= GetTabSize() - (m_nOffsetChar
+ xpos
) % GetTabSize();
1511 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
, y
);
1512 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1513 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
-4);
1514 pDC
->MoveTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-2, y
);
1515 pDC
->LineTo((xpos
+ nSpaces
) * GetCharWidth() + rc
.left
-6, y
+4);
1517 pDC
->SelectObject(oldPen
);
1523 CPen
* oldPen
= pDC
->SelectObject(&pen2
);
1524 pDC
->MoveTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2-1, y
);
1525 pDC
->LineTo(xpos
* GetCharWidth() + rc
.left
+ GetCharWidth()/2+1, y
);
1527 pDC
->SelectObject(oldPen
);
1537 DrawBlockLine(pDC
, rc
, nLineIndex
);
1538 DrawLineEnding(pDC
, rc
, nLineIndex
, origin
);
1541 void CBaseView::ExpandChars(LPCTSTR pszChars
, int nOffset
, int nCount
, CString
&line
)
1549 int nTabSize
= GetTabSize();
1551 int nActualOffset
= 0;
1552 for (int i
=0; i
<nOffset
; i
++)
1554 if (pszChars
[i
] == _T('\t'))
1555 nActualOffset
+= (nTabSize
- nActualOffset
% nTabSize
);
1560 pszChars
+= nOffset
;
1561 int nLength
= nCount
;
1564 for (int i
=0; i
<nLength
; i
++)
1566 if (pszChars
[i
] == _T('\t'))
1570 LPTSTR pszBuf
= line
.GetBuffer(nLength
+ nTabCount
* (nTabSize
- 1) + 1);
1572 if (nTabCount
> 0 || m_bViewWhitespace
)
1574 for (int i
=0; i
<nLength
; i
++)
1576 if (pszChars
[i
] == _T('\t'))
1578 int nSpaces
= nTabSize
- (nActualOffset
+ nCurPos
) % nTabSize
;
1581 pszBuf
[nCurPos
++] = _T(' ');
1587 pszBuf
[nCurPos
] = pszChars
[i
];
1594 memcpy(pszBuf
, pszChars
, sizeof(TCHAR
) * nLength
);
1597 pszBuf
[nCurPos
] = 0;
1598 line
.ReleaseBuffer();
1601 void CBaseView::ScrollAllToLine(int nNewTopLine
, BOOL bTrackScrollBar
)
1603 if ((m_pwndLeft
)&&(m_pwndRight
))
1605 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1606 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1611 m_pwndLeft
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1613 m_pwndRight
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1616 m_pwndBottom
->ScrollToLine(nNewTopLine
, bTrackScrollBar
);
1618 m_pwndLocator
->Invalidate();
1621 void CBaseView::GoToLine(int nNewLine
, BOOL bAll
)
1623 //almost the same as ScrollAllToLine, but try to put the line in the
1624 //middle of the view, not on top
1625 int nNewTopLine
= nNewLine
- GetScreenLines()/2;
1626 if (nNewTopLine
< 0)
1630 if (nNewTopLine
>= m_pViewData
->GetCount())
1631 nNewTopLine
= m_pViewData
->GetCount()-1;
1633 ScrollAllToLine(nNewTopLine
);
1635 ScrollToLine(nNewTopLine
);
1639 BOOL
CBaseView::OnEraseBkgnd(CDC
* /*pDC*/)
1644 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct
)
1646 if (CView::OnCreate(lpCreateStruct
) == -1)
1649 memset(&m_lfBaseFont
, 0, sizeof(m_lfBaseFont
));
1650 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1651 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1652 m_lfBaseFont
.lfHeight
= 0;
1653 m_lfBaseFont
.lfWeight
= FW_NORMAL
;
1654 m_lfBaseFont
.lfItalic
= FALSE
;
1655 m_lfBaseFont
.lfCharSet
= DEFAULT_CHARSET
;
1656 m_lfBaseFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1657 m_lfBaseFont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1658 m_lfBaseFont
.lfQuality
= DEFAULT_QUALITY
;
1659 m_lfBaseFont
.lfPitchAndFamily
= DEFAULT_PITCH
;
1664 void CBaseView::OnDestroy()
1667 for (int i
=0; i
<MAXFONTS
; i
++)
1669 if (m_apFonts
[i
] != NULL
)
1671 m_apFonts
[i
]->DeleteObject();
1672 delete m_apFonts
[i
];
1673 m_apFonts
[i
] = NULL
;
1676 if (m_pCacheBitmap
!= NULL
)
1678 delete m_pCacheBitmap
;
1679 m_pCacheBitmap
= NULL
;
1683 void CBaseView::OnSize(UINT nType
, int cx
, int cy
)
1685 if (m_pCacheBitmap
!= NULL
)
1687 m_pCacheBitmap
->DeleteObject();
1688 delete m_pCacheBitmap
;
1689 m_pCacheBitmap
= NULL
;
1691 // make sure the view header is redrawn
1693 GetClientRect(&rcScroll
);
1694 rcScroll
.bottom
= GetLineHeight()+HEADERHEIGHT
;
1695 InvalidateRect(&rcScroll
, FALSE
);
1697 m_nScreenLines
= -1;
1698 m_nScreenChars
= -1;
1699 RecalcVertScrollBar();
1700 RecalcHorzScrollBar();
1701 CView::OnSize(nType
, cx
, cy
);
1704 BOOL
CBaseView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint pt
)
1707 m_pwndLeft
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1709 m_pwndRight
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1711 m_pwndBottom
->OnDoMouseWheel(nFlags
, zDelta
, pt
);
1713 m_pwndLocator
->Invalidate();
1714 return CView::OnMouseWheel(nFlags
, zDelta
, pt
);
1717 void CBaseView::OnDoMouseWheel(UINT
/*nFlags*/, short zDelta
, CPoint
/*pt*/)
1719 if (GetKeyState(VK_CONTROL
)&0x8000)
1721 // Ctrl-Wheel scrolls sideways
1722 ScrollSide(-zDelta
/30);
1726 int nLineCount
= GetLineCount();
1727 int nTopLine
= m_nTopLine
;
1728 nTopLine
-= (zDelta
/30);
1731 if (nTopLine
>= nLineCount
)
1732 nTopLine
= nLineCount
- 1;
1733 ScrollToLine(nTopLine
, TRUE
);
1737 BOOL
CBaseView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1739 if (nHitTest
== HTCLIENT
)
1741 ::SetCursor(::LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
))); // Set To Arrow Cursor
1744 return CView::OnSetCursor(pWnd
, nHitTest
, message
);
1747 void CBaseView::OnKillFocus(CWnd
* pNewWnd
)
1749 CView::OnKillFocus(pNewWnd
);
1755 void CBaseView::OnSetFocus(CWnd
* pOldWnd
)
1757 CView::OnSetFocus(pOldWnd
);
1763 int CBaseView::GetLineFromPoint(CPoint point
)
1765 ScreenToClient(&point
);
1766 return (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
1769 bool CBaseView::OnContextMenu(CPoint
/*point*/, int /*nLine*/, DiffStates
/*state*/)
1774 void CBaseView::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
1776 int nLine
= GetLineFromPoint(point
);
1780 if (m_nSelBlockEnd
>= GetLineCount())
1781 m_nSelBlockEnd
= GetLineCount()-1;
1782 if ((nLine
<= m_pViewData
->GetCount())&&(nLine
> m_nTopLine
))
1784 int nIndex
= nLine
- 1;
1785 DiffStates state
= m_pViewData
->GetState(nIndex
);
1786 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1788 // if there's nothing selected, or if the selection is outside the window then
1789 // select the diff block under the cursor.
1790 if (((m_nSelBlockStart
<0)&&(m_nSelBlockEnd
<0))||
1791 ((m_nSelBlockEnd
< m_nTopLine
)||(m_nSelBlockStart
> m_nTopLine
+m_nScreenLines
)))
1800 if (state
!= m_pViewData
->GetState(--nIndex
))
1803 m_nSelBlockStart
= nIndex
+1;
1804 while (nIndex
< (m_pViewData
->GetCount()-1))
1806 if (state
!= m_pViewData
->GetState(++nIndex
))
1809 if ((nIndex
== (m_pViewData
->GetCount()-1))&&(state
== m_pViewData
->GetState(nIndex
)))
1810 m_nSelBlockEnd
= nIndex
;
1812 m_nSelBlockEnd
= nIndex
-1;
1813 SetupSelection(m_nSelBlockStart
, m_nSelBlockEnd
);
1815 m_ptCaretPos
.y
= nLine
- 1;
1819 if (((state
== DIFFSTATE_NORMAL
)||(state
== DIFFSTATE_UNKNOWN
)) &&
1820 (m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))
1822 // find a more 'relevant' state in the selection
1823 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; ++i
)
1825 state
= m_pViewData
->GetState(i
);
1826 if ((state
!= DIFFSTATE_NORMAL
) && (state
!= DIFFSTATE_UNKNOWN
))
1830 bool bKeepSelection
= OnContextMenu(point
, nLine
, state
);
1831 if (! bKeepSelection
)
1837 void CBaseView::RefreshViews()
1841 m_pwndLeft
->UpdateStatusBar();
1842 m_pwndLeft
->Invalidate();
1846 m_pwndRight
->UpdateStatusBar();
1847 m_pwndRight
->Invalidate();
1851 m_pwndBottom
->UpdateStatusBar();
1852 m_pwndBottom
->Invalidate();
1855 m_pwndLocator
->Invalidate();
1858 void CBaseView::GoToFirstDifference()
1861 SelectNextBlock(1, false, false);
1864 void CBaseView::HiglightLines(int start
, int end
/* = -1 */)
1867 m_nSelBlockStart
= start
;
1870 m_nSelBlockEnd
= end
;
1872 m_ptCaretPos
.y
= start
;
1877 void CBaseView::SetupSelection(int start
, int end
)
1879 if (IsBottomViewGood())
1881 m_pwndBottom
->m_nSelBlockStart
= start
;
1882 m_pwndBottom
->m_nSelBlockEnd
= end
;
1883 m_pwndBottom
->Invalidate();
1885 if (IsLeftViewGood())
1887 m_pwndLeft
->m_nSelBlockStart
= start
;
1888 m_pwndLeft
->m_nSelBlockEnd
= end
;
1889 m_pwndLeft
->Invalidate();
1891 if (IsRightViewGood())
1893 m_pwndRight
->m_nSelBlockStart
= start
;
1894 m_pwndRight
->m_nSelBlockEnd
= end
;
1895 m_pwndRight
->Invalidate();
1899 void CBaseView::OnMergePreviousconflict()
1901 SelectNextBlock(-1, true);
1904 void CBaseView::OnMergeNextconflict()
1906 SelectNextBlock(1, true);
1909 void CBaseView::OnMergeNextdifference()
1911 SelectNextBlock(1, false);
1914 void CBaseView::OnMergePreviousdifference()
1916 SelectNextBlock(-1, false);
1919 void CBaseView::SelectNextBlock(int nDirection
, bool bConflict
, bool bSkipEndOfCurrentBlock
/* = true */)
1924 if (m_pViewData
->GetCount() == 0)
1927 int nCenterPos
= m_ptCaretPos
.y
;
1930 nLimit
= m_pViewData
->GetCount() - 1;
1932 if (nCenterPos
>= m_pViewData
->GetCount())
1933 nCenterPos
= m_pViewData
->GetCount()-1;
1935 if (bSkipEndOfCurrentBlock
)
1937 // Find end of current block
1938 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1939 while ((nCenterPos
!= nLimit
) &&
1940 (m_pViewData
->GetState(nCenterPos
)==state
))
1941 nCenterPos
+= nDirection
;
1944 // Find next diff/conflict block
1945 while (nCenterPos
!= nLimit
)
1947 DiffStates linestate
= m_pViewData
->GetState(nCenterPos
);
1949 (linestate
!= DIFFSTATE_NORMAL
) &&
1950 (linestate
!= DIFFSTATE_UNKNOWN
))
1953 ((linestate
== DIFFSTATE_CONFLICTADDED
) ||
1954 (linestate
== DIFFSTATE_CONFLICTED_IGNORED
) ||
1955 (linestate
== DIFFSTATE_CONFLICTED
) ||
1956 (linestate
== DIFFSTATE_CONFLICTEMPTY
)))
1959 nCenterPos
+= nDirection
;
1962 // Find end of new block
1963 DiffStates state
= m_pViewData
->GetState(nCenterPos
);
1964 int nBlockEnd
= nCenterPos
;
1965 while ((nBlockEnd
!= nLimit
) &&
1966 (state
== m_pViewData
->GetState(nBlockEnd
+ nDirection
)))
1967 nBlockEnd
+= nDirection
;
1969 int nTopPos
= nCenterPos
- (GetScreenLines()/2);
1974 m_ptCaretPos
.y
= nCenterPos
;
1977 SetupSelection(nCenterPos
, nBlockEnd
);
1979 SetupSelection(nBlockEnd
, nCenterPos
);
1981 ScrollAllToLine(nTopPos
, FALSE
);
1982 RecalcAllVertScrollBars(TRUE
);
1983 m_nCaretGoalPos
= 0;
1985 ShowDiffLines(nCenterPos
);
1988 BOOL
CBaseView::OnToolTipNotify(UINT
/*id*/, NMHDR
*pNMHDR
, LRESULT
*pResult
)
1990 // need to handle both ANSI and UNICODE versions of the message
1991 TOOLTIPTEXTA
* pTTTA
= (TOOLTIPTEXTA
*)pNMHDR
;
1992 TOOLTIPTEXTW
* pTTTW
= (TOOLTIPTEXTW
*)pNMHDR
;
1994 UINT nID
= (UINT
)pNMHDR
->idFrom
;
1995 if (pNMHDR
->code
== TTN_NEEDTEXTA
&& (pTTTA
->uFlags
& TTF_IDISHWND
) ||
1996 pNMHDR
->code
== TTN_NEEDTEXTW
&& (pTTTW
->uFlags
& TTF_IDISHWND
))
1998 // idFrom is actually the HWND of the tool
1999 nID
= ::GetDlgCtrlID((HWND
)nID
);
2002 if (pNMHDR
->idFrom
== (UINT
)m_hWnd
)
2004 if (m_sWindowName
.Left(2).Compare(_T("* "))==0)
2006 strTipText
= m_sWindowName
.Mid(2) + _T("\r\n") + m_sFullFilePath
;
2010 strTipText
= m_sWindowName
+ _T("\r\n") + m_sFullFilePath
;
2017 if (strTipText
.IsEmpty())
2020 if (pNMHDR
->code
== TTN_NEEDTEXTA
)
2022 pTTTA
->lpszText
= m_szTip
;
2023 WideCharToMultiByte(CP_ACP
, 0, strTipText
, -1, m_szTip
, strTipText
.GetLength()+1, 0, 0);
2027 lstrcpyn(m_wszTip
, strTipText
, strTipText
.GetLength()+1);
2028 pTTTW
->lpszText
= m_wszTip
;
2031 return TRUE
; // message was handled
2035 INT_PTR
CBaseView::OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const
2038 GetClientRect(rcClient
);
2039 CRect
textrect(rcClient
.left
, rcClient
.top
, rcClient
.Width(), m_nLineHeight
+HEADERHEIGHT
);
2040 if (textrect
.PtInRect(point
))
2042 // inside the header part of the view (showing the filename)
2043 pTI
->hwnd
= this->m_hWnd
;
2044 this->GetClientRect(&pTI
->rect
);
2045 pTI
->uFlags
|= TTF_ALWAYSTIP
| TTF_IDISHWND
;
2046 pTI
->uId
= (UINT
)m_hWnd
;
2047 pTI
->lpszText
= LPSTR_TEXTCALLBACK
;
2049 // we want multi line tooltips
2050 CToolTipCtrl
* pToolTip
= AfxGetModuleThreadState()->m_pToolTip
;
2051 if (pToolTip
->GetSafeHwnd() != NULL
)
2053 pToolTip
->SetMaxTipWidth(INT_MAX
);
2061 void CBaseView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2063 bool bControl
= !!(GetKeyState(VK_CONTROL
)&0x8000);
2064 bool bShift
= !!(GetKeyState(VK_SHIFT
)&0x8000);
2069 m_ptCaretPos
.y
-= GetScreenLines();
2070 m_ptCaretPos
.y
= max(m_ptCaretPos
.y
, 0);
2071 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2077 EnsureCaretVisible();
2078 ShowDiffLines(m_ptCaretPos
.y
);
2083 m_ptCaretPos
.y
+= GetScreenLines();
2084 if (m_ptCaretPos
.y
>= GetLineCount())
2085 m_ptCaretPos
.y
= GetLineCount()-1;
2086 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2092 EnsureCaretVisible();
2093 ShowDiffLines(m_ptCaretPos
.y
);
2103 m_nCaretGoalPos
= 0;
2113 m_nCaretGoalPos
= 0;
2118 EnsureCaretVisible();
2127 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2128 m_ptCaretPos
.y
= GetLineCount()-1;
2129 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2139 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2145 EnsureCaretVisible();
2155 if (! HasTextSelection()) {
2156 if (m_ptCaretPos
.y
== 0 && m_ptCaretPos
.x
== 0)
2158 m_ptSelectionEndPos
= m_ptCaretPos
;
2160 m_ptSelectionStartPos
= m_ptCaretPos
;
2162 RemoveSelectedText();
2170 if (! HasTextSelection()) {
2171 if (! MoveCaretRight())
2173 m_ptSelectionEndPos
= m_ptCaretPos
;
2175 m_ptSelectionStartPos
= m_ptCaretPos
;
2177 RemoveSelectedText();
2181 CView::OnKeyDown(nChar
, nRepCnt
, nFlags
);
2184 void CBaseView::OnLButtonDown(UINT nFlags
, CPoint point
)
2186 int nClickedLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2187 nClickedLine
--; //we need the index
2188 if ((nClickedLine
>= m_nTopLine
)&&(nClickedLine
< GetLineCount()))
2190 m_ptCaretPos
.y
= nClickedLine
;
2191 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2194 if (nFlags
& MK_SHIFT
)
2199 SetupSelection(m_ptCaretPos
.y
, m_ptCaretPos
.y
);
2207 CView::OnLButtonDown(nFlags
, point
);
2210 void CBaseView::OnEditCopy()
2212 if ((m_ptSelectionStartPos
.x
== m_ptSelectionEndPos
.x
)&&(m_ptSelectionStartPos
.y
== m_ptSelectionEndPos
.y
))
2214 // first store the selected lines in one CString
2216 for (int i
=m_ptSelectionStartPos
.y
; i
<=m_ptSelectionEndPos
.y
; i
++)
2218 switch (m_pViewData
->GetState(i
))
2220 case DIFFSTATE_EMPTY
:
2222 case DIFFSTATE_UNKNOWN
:
2223 case DIFFSTATE_NORMAL
:
2224 case DIFFSTATE_REMOVED
:
2225 case DIFFSTATE_REMOVEDWHITESPACE
:
2226 case DIFFSTATE_ADDED
:
2227 case DIFFSTATE_ADDEDWHITESPACE
:
2228 case DIFFSTATE_WHITESPACE
:
2229 case DIFFSTATE_WHITESPACE_DIFF
:
2230 case DIFFSTATE_CONFLICTED
:
2231 case DIFFSTATE_CONFLICTED_IGNORED
:
2232 case DIFFSTATE_CONFLICTADDED
:
2233 case DIFFSTATE_CONFLICTEMPTY
:
2234 case DIFFSTATE_CONFLICTRESOLVED
:
2235 case DIFFSTATE_IDENTICALREMOVED
:
2236 case DIFFSTATE_IDENTICALADDED
:
2237 case DIFFSTATE_THEIRSREMOVED
:
2238 case DIFFSTATE_THEIRSADDED
:
2239 case DIFFSTATE_YOURSREMOVED
:
2240 case DIFFSTATE_YOURSADDED
:
2241 case DIFFSTATE_EDITED
:
2242 sCopyData
+= m_pViewData
->GetLine(i
);
2243 sCopyData
+= _T("\r\n");
2247 // remove the last \r\n
2248 sCopyData
= sCopyData
.Left(sCopyData
.GetLength()-2);
2249 // remove the non-selected chars from the first line
2250 sCopyData
= sCopyData
.Mid(m_ptSelectionStartPos
.x
);
2251 // remove the non-selected chars from the last line
2252 int lastLinePos
= sCopyData
.ReverseFind('\n');
2254 if (lastLinePos
== 0)
2255 lastLinePos
-= m_ptSelectionStartPos
.x
;
2256 sCopyData
= sCopyData
.Left(lastLinePos
+m_ptSelectionEndPos
.x
);
2257 if (!sCopyData
.IsEmpty())
2259 CStringUtils::WriteAsciiStringToClipboard(sCopyData
, m_hWnd
);
2263 void CBaseView::OnMouseMove(UINT nFlags
, CPoint point
)
2265 if (m_pMainFrame
->m_nMoveMovesToIgnore
> 0) {
2266 --m_pMainFrame
->m_nMoveMovesToIgnore
;
2267 CView::OnMouseMove(nFlags
, point
);
2271 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2272 nMouseLine
--; //we need the index
2273 if (nMouseLine
< -1)
2277 ShowDiffLines(nMouseLine
);
2279 KillTimer(IDT_SCROLLTIMER
);
2280 if (nFlags
& MK_LBUTTON
)
2282 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2283 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2284 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2285 if (((m_nSelBlockStart
>= 0)&&(m_nSelBlockEnd
>= 0))&&
2286 ((nMouseLine
>= m_nTopLine
)&&(nMouseLine
< GetLineCount())))
2288 m_ptCaretPos
.y
= nMouseLine
;
2289 m_ptCaretPos
.x
= charIndex
;
2296 if (nMouseLine
< m_nTopLine
)
2298 ScrollToLine(m_nTopLine
-1, TRUE
);
2299 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2301 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2303 ScrollToLine(m_nTopLine
+1, TRUE
);
2304 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2306 if (charIndex
<= m_nOffsetChar
)
2309 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2311 if (charIndex
>= (GetScreenChars()+m_nOffsetChar
))
2314 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2318 if (!m_bMouseWithin
)
2320 m_bMouseWithin
= TRUE
;
2321 TRACKMOUSEEVENT tme
;
2322 tme
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2323 tme
.dwFlags
= TME_LEAVE
;
2324 tme
.hwndTrack
= m_hWnd
;
2325 _TrackMouseEvent(&tme
);
2328 CView::OnMouseMove(nFlags
, point
);
2331 void CBaseView::OnMouseLeave()
2334 m_bMouseWithin
= FALSE
;
2335 KillTimer(IDT_SCROLLTIMER
);
2336 CView::OnMouseLeave();
2339 void CBaseView::OnTimer(UINT_PTR nIDEvent
)
2341 if (nIDEvent
== IDT_SCROLLTIMER
)
2344 GetCursorPos(&point
);
2345 ScreenToClient(&point
);
2346 int nMouseLine
= (((point
.y
- HEADERHEIGHT
) / GetLineHeight()) + m_nTopLine
);
2347 nMouseLine
--; //we need the index
2348 if (nMouseLine
< -1)
2352 if (GetKeyState(VK_LBUTTON
)&0x8000)
2354 int saveMouseLine
= nMouseLine
>= 0 ? nMouseLine
: 0;
2355 saveMouseLine
= saveMouseLine
< GetLineCount() ? saveMouseLine
: GetLineCount() - 1;
2356 int charIndex
= CalculateCharIndex(saveMouseLine
, m_nOffsetChar
+ (point
.x
- GetMarginWidth()) / GetCharWidth());
2357 if (nMouseLine
< m_nTopLine
)
2359 ScrollToLine(m_nTopLine
-1, TRUE
);
2360 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2362 if (nMouseLine
>= m_nTopLine
+ GetScreenLines())
2364 ScrollToLine(m_nTopLine
+1, TRUE
);
2365 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2367 if (charIndex
<= m_nOffsetChar
)
2370 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2372 if (charIndex
>= GetScreenChars())
2375 SetTimer(IDT_SCROLLTIMER
, 20, NULL
);
2381 CView::OnTimer(nIDEvent
);
2384 void CBaseView::SelectLines(int nLine1
, int nLine2
)
2388 m_nSelBlockStart
= nLine1
;
2389 m_nSelBlockEnd
= nLine2
;
2393 void CBaseView::ShowDiffLines(int nLine
)
2395 if ((nLine
>= m_nTopLine
)&&(nLine
< GetLineCount()))
2397 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
)&&(m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
)&&(!m_pMainFrame
->m_bOneWay
))
2399 nLine
= (nLine
> m_pwndRight
->m_pViewData
->GetCount() ? -1 : nLine
);
2400 nLine
= (nLine
> m_pwndLeft
->m_pViewData
->GetCount() ? -1 : nLine
);
2404 if (nLine
!= m_nMouseLine
)
2406 m_nMouseLine
= nLine
;
2407 if (nLine
>= GetLineCount())
2409 m_pwndLineDiffBar
->ShowLines(nLine
);
2416 m_pwndLineDiffBar
->ShowLines(nLine
);
2420 void CBaseView::UseTheirAndYourBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2422 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2424 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2426 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2427 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndLeft
->m_pViewData
->GetLine(i
));
2428 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2429 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndLeft
->m_pViewData
->GetState(i
));
2430 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, EOL_AUTOLINE
);
2431 if (m_pwndBottom
->IsLineConflicted(i
))
2433 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2434 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2436 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2438 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2441 // your block is done, now insert their block
2442 int index
= m_nSelBlockEnd
+1;
2443 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2445 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2446 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndRight
->m_pViewData
->GetData(i
));
2447 if (m_pwndBottom
->IsLineConflicted(index
))
2449 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2450 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2452 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2454 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2457 // adjust line numbers
2458 for (int i
=m_nSelBlockEnd
+1; i
<GetLineCount(); ++i
)
2460 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2462 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2465 // now insert an empty block in both yours and theirs
2466 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2468 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2469 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2470 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2471 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2473 RecalcAllVertScrollBars();
2474 m_pwndBottom
->SetModified();
2475 m_pwndLeft
->SetModified();
2476 m_pwndRight
->SetModified();
2479 void CBaseView::UseYourAndTheirBlock(viewstate
&rightstate
, viewstate
&bottomstate
, viewstate
&leftstate
)
2481 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2483 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2485 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2486 m_pwndBottom
->m_pViewData
->SetLine(i
, m_pwndRight
->m_pViewData
->GetLine(i
));
2487 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2488 m_pwndBottom
->m_pViewData
->SetState(i
, m_pwndRight
->m_pViewData
->GetState(i
));
2489 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2490 m_pwndBottom
->m_pViewData
->SetLineEnding(i
, EOL_AUTOLINE
);
2491 if (m_pwndBottom
->IsLineConflicted(i
))
2493 if (m_pwndRight
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2494 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2496 m_pwndBottom
->m_pViewData
->SetState(i
, DIFFSTATE_CONFLICTRESOLVED
);
2498 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2501 // your block is done, now insert their block
2502 int index
= m_nSelBlockEnd
+1;
2503 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2505 bottomstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2506 m_pwndBottom
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2507 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2508 if (m_pwndBottom
->IsLineConflicted(index
))
2510 if (m_pwndLeft
->m_pViewData
->GetState(i
) == DIFFSTATE_CONFLICTEMPTY
)
2511 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVEDEMPTY
);
2513 m_pwndBottom
->m_pViewData
->SetState(index
, DIFFSTATE_CONFLICTRESOLVED
);
2515 m_pwndLeft
->m_pViewData
->SetState(i
, DIFFSTATE_THEIRSADDED
);
2518 // adjust line numbers
2519 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndBottom
->GetLineCount(); ++i
)
2521 long oldline
= (long)m_pwndBottom
->m_pViewData
->GetLineNumber(i
);
2523 m_pwndBottom
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2526 // now insert an empty block in both yours and theirs
2527 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2529 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2530 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2531 m_pwndRight
->m_pViewData
->InsertData(m_nSelBlockEnd
+1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2532 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2535 RecalcAllVertScrollBars();
2536 m_pwndBottom
->SetModified();
2537 m_pwndLeft
->SetModified();
2538 m_pwndRight
->SetModified();
2541 void CBaseView::UseBothRightFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2543 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2545 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2547 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2548 m_pwndRight
->m_pViewData
->SetState(i
, DIFFSTATE_YOURSADDED
);
2551 // your block is done, now insert their block
2552 int index
= m_nSelBlockEnd
+1;
2553 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2555 rightstate
.addedlines
.push_back(m_nSelBlockEnd
+1);
2556 m_pwndRight
->m_pViewData
->InsertData(index
, m_pwndLeft
->m_pViewData
->GetData(i
));
2557 m_pwndRight
->m_pViewData
->SetState(index
++, DIFFSTATE_THEIRSADDED
);
2559 // adjust line numbers
2561 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2563 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2565 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(index
-m_nSelBlockEnd
));
2568 // now insert an empty block in the left view
2569 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2571 leftstate
.addedlines
.push_back(m_nSelBlockStart
);
2572 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockStart
, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2574 RecalcAllVertScrollBars();
2575 m_pwndLeft
->SetModified();
2576 m_pwndRight
->SetModified();
2579 void CBaseView::UseBothLeftFirst(viewstate
&rightstate
, viewstate
&leftstate
)
2581 if ((m_nSelBlockStart
== -1)||(m_nSelBlockEnd
== -1))
2583 // get line number from just before the block
2584 long linenumber
= 0;
2585 if (m_nSelBlockStart
> 0)
2586 linenumber
= m_pwndRight
->m_pViewData
->GetLineNumber(m_nSelBlockStart
-1);
2588 for (int i
=m_nSelBlockStart
; i
<=m_nSelBlockEnd
; i
++)
2590 rightstate
.addedlines
.push_back(m_nSelBlockStart
);
2591 m_pwndRight
->m_pViewData
->InsertData(i
, m_pwndLeft
->m_pViewData
->GetLine(i
), DIFFSTATE_THEIRSADDED
, linenumber
++, m_pwndLeft
->m_pViewData
->GetLineEnding(i
));
2593 // adjust line numbers
2594 for (int i
=m_nSelBlockEnd
+1; i
<m_pwndRight
->GetLineCount(); ++i
)
2596 long oldline
= (long)m_pwndRight
->m_pViewData
->GetLineNumber(i
);
2598 m_pwndRight
->m_pViewData
->SetLineNumber(i
, oldline
+(m_nSelBlockEnd
-m_nSelBlockStart
)+1);
2601 // now insert an empty block in left view
2602 for (int emptyblocks
=0; emptyblocks
< m_nSelBlockEnd
-m_nSelBlockStart
+1; ++emptyblocks
)
2604 leftstate
.addedlines
.push_back(m_nSelBlockEnd
+ 1);
2605 m_pwndLeft
->m_pViewData
->InsertData(m_nSelBlockEnd
+ 1, _T(""), DIFFSTATE_EMPTY
, -1, EOL_NOENDING
);
2607 RecalcAllVertScrollBars();
2608 m_pwndLeft
->SetModified();
2609 m_pwndRight
->SetModified();
2612 void CBaseView::UpdateCaret()
2614 if (m_ptCaretPos
.y
>= GetLineCount())
2615 m_ptCaretPos
.y
= GetLineCount()-1;
2616 if (m_ptCaretPos
.y
< 0)
2618 if (m_ptCaretPos
.x
> GetLineLength(m_ptCaretPos
.y
))
2619 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2620 if (m_ptCaretPos
.x
< 0)
2623 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2625 if (m_bFocused
&& !m_bCaretHidden
&&
2626 m_ptCaretPos
.y
>= m_nTopLine
&&
2627 m_ptCaretPos
.y
< (m_nTopLine
+GetScreenLines()) &&
2628 nCaretOffset
>= m_nOffsetChar
&&
2629 nCaretOffset
< (m_nOffsetChar
+GetScreenChars()))
2631 CreateSolidCaret(2, GetLineHeight());
2632 SetCaretPos(TextToClient(m_ptCaretPos
));
2641 void CBaseView::EnsureCaretVisible()
2643 int nCaretOffset
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2645 if (m_ptCaretPos
.y
< m_nTopLine
)
2646 ScrollAllToLine(m_ptCaretPos
.y
);
2647 if (m_ptCaretPos
.y
>= (m_nTopLine
+GetScreenLines()))
2648 ScrollAllToLine(m_ptCaretPos
.y
-GetScreenLines()+1);
2649 if (nCaretOffset
< m_nOffsetChar
)
2650 ScrollToChar(nCaretOffset
);
2651 if (nCaretOffset
> (m_nOffsetChar
+GetScreenChars()-1))
2652 ScrollToChar(nCaretOffset
-GetScreenChars()+1);
2655 int CBaseView::CalculateActualOffset(int nLineIndex
, int nCharIndex
) const
2657 int nLength
= GetLineLength(nLineIndex
);
2658 ASSERT(nCharIndex
>= 0);
2659 if (nCharIndex
> nLength
)
2660 nCharIndex
= nLength
;
2661 LPCTSTR pszChars
= GetLineChars(nLineIndex
);
2663 int nTabSize
= GetTabSize();
2664 for (int I
= 0; I
< nCharIndex
; I
++)
2666 if (pszChars
[I
] == _T('\t'))
2667 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2674 int CBaseView::CalculateCharIndex(int nLineIndex
, int nActualOffset
) const
2676 int nLength
= GetLineLength(nLineIndex
);
2677 LPCTSTR pszLine
= GetLineChars(nLineIndex
);
2680 int nTabSize
= GetTabSize();
2681 while (nOffset
< nActualOffset
&& nIndex
< nLength
)
2683 if (pszLine
[nIndex
] == _T('\t'))
2684 nOffset
+= (nTabSize
- nOffset
% nTabSize
);
2692 POINT
CBaseView::TextToClient(const POINT
& point
)
2695 pt
.y
= max(0, (point
.y
- m_nTopLine
) * GetLineHeight());
2696 pt
.x
= CalculateActualOffset(point
.y
, point
.x
);
2698 pt
.x
= (pt
.x
- m_nOffsetChar
) * GetCharWidth() + GetMarginWidth();
2699 pt
.y
= (pt
.y
+ GetLineHeight() + HEADERHEIGHT
);
2703 void CBaseView::OnChar(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
2705 CView::OnChar(nChar
, nRepCnt
, nFlags
);
2710 if ((::GetKeyState(VK_LBUTTON
) & 0x8000) != 0 ||
2711 (::GetKeyState(VK_RBUTTON
) & 0x8000) != 0)
2714 if ((nChar
> 31)||(nChar
== VK_TAB
))
2716 RemoveSelectedText();
2717 AddUndoLine(m_ptCaretPos
.y
);
2718 CString sLine
= GetLineChars(m_ptCaretPos
.y
);
2719 sLine
.Insert(m_ptCaretPos
.x
, (wchar_t)nChar
);
2720 m_pViewData
->SetLine(m_ptCaretPos
.y
, sLine
);
2721 m_pViewData
->SetState(m_ptCaretPos
.y
, DIFFSTATE_EDITED
);
2725 else if (nChar
== VK_RETURN
)
2727 // insert a new, fresh and empty line below the cursor
2728 RemoveSelectedText();
2729 AddUndoLine(m_ptCaretPos
.y
, true);
2730 // move the cursor to the new line
2736 return; // Unknown control character -- ignore it.
2738 EnsureCaretVisible();
2744 void CBaseView::AddUndoLine(int nLine
, bool bAddEmptyLine
)
2746 viewstate leftstate
;
2747 viewstate rightstate
;
2748 viewstate bottomstate
;
2749 leftstate
.AddLineFormView(m_pwndLeft
, nLine
, bAddEmptyLine
);
2750 rightstate
.AddLineFormView(m_pwndRight
, nLine
, bAddEmptyLine
);
2751 bottomstate
.AddLineFormView(m_pwndBottom
, nLine
, bAddEmptyLine
);
2752 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2755 void CBaseView::AddEmptyLine(int nLineIndex
)
2757 if (m_pViewData
== NULL
)
2759 if (!m_bCaretHidden
)
2761 CString sPartLine
= GetLineChars(nLineIndex
);
2762 m_pViewData
->SetLine(nLineIndex
, sPartLine
.Left(m_ptCaretPos
.x
));
2763 sPartLine
= sPartLine
.Mid(m_ptCaretPos
.x
);
2764 m_pViewData
->InsertData(nLineIndex
+1, sPartLine
, DIFFSTATE_EDITED
, -1, m_pViewData
->GetLineEnding(nLineIndex
));
2767 m_pViewData
->InsertData(nLineIndex
+1, _T(""), DIFFSTATE_EDITED
, -1, m_pViewData
->GetLineEnding(nLineIndex
));
2771 void CBaseView::RemoveLine(int nLineIndex
)
2773 if (m_pViewData
== NULL
)
2775 m_pViewData
->RemoveData(nLineIndex
);
2776 if (m_ptCaretPos
.y
>= GetLineCount())
2777 m_ptCaretPos
.y
= GetLineCount()-1;
2781 void CBaseView::RemoveSelectedText()
2783 if (m_pViewData
== NULL
)
2785 if (!HasTextSelection())
2788 viewstate rightstate
;
2789 viewstate bottomstate
;
2790 viewstate leftstate
;
2791 std::vector
<LONG
> linestoremove
;
2792 for (LONG i
= m_ptSelectionStartPos
.y
; i
<= m_ptSelectionEndPos
.y
; ++i
)
2794 if (i
== m_ptSelectionStartPos
.y
)
2796 CString sLine
= GetLineChars(m_ptSelectionStartPos
.y
);
2798 if (i
== m_ptSelectionStartPos
.y
)
2800 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2802 leftstate
.difflines
[i
] = m_pwndLeft
->m_pViewData
->GetLine(i
);
2803 leftstate
.linestates
[i
] = m_pwndLeft
->m_pViewData
->GetState(i
);
2805 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2807 rightstate
.difflines
[i
] = m_pwndRight
->m_pViewData
->GetLine(i
);
2808 rightstate
.linestates
[i
] = m_pwndRight
->m_pViewData
->GetState(i
);
2810 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2812 bottomstate
.difflines
[i
] = m_pwndBottom
->m_pViewData
->GetLine(i
);
2813 bottomstate
.linestates
[i
] = m_pwndBottom
->m_pViewData
->GetState(i
);
2815 newLine
= sLine
.Left(m_ptSelectionStartPos
.x
);
2816 sLine
= GetLineChars(m_ptSelectionEndPos
.y
);
2817 newLine
= newLine
+ sLine
.Mid(m_ptSelectionEndPos
.x
);
2819 m_pViewData
->SetLine(i
, newLine
);
2820 m_pViewData
->SetState(i
, DIFFSTATE_EDITED
);
2825 if ((m_pwndLeft
)&&(m_pwndLeft
->m_pViewData
))
2827 leftstate
.removedlines
[i
] = m_pwndLeft
->m_pViewData
->GetData(i
);
2829 if ((m_pwndRight
)&&(m_pwndRight
->m_pViewData
))
2831 rightstate
.removedlines
[i
] = m_pwndRight
->m_pViewData
->GetData(i
);
2833 if ((m_pwndBottom
)&&(m_pwndBottom
->m_pViewData
))
2835 bottomstate
.removedlines
[i
] = m_pwndBottom
->m_pViewData
->GetData(i
);
2837 linestoremove
.push_back(i
);
2840 CUndo::GetInstance().AddState(leftstate
, rightstate
, bottomstate
, m_ptCaretPos
);
2841 // remove the lines at the end, to avoid problems with line indexes
2842 if (linestoremove
.size())
2844 std::vector
<LONG
>::const_iterator it
= linestoremove
.begin();
2845 int nLineToRemove
= *it
;
2846 for ( ; it
!= linestoremove
.end(); ++it
)
2849 m_pwndLeft
->RemoveLine(nLineToRemove
);
2851 m_pwndRight
->RemoveLine(nLineToRemove
);
2853 m_pwndBottom
->RemoveLine(nLineToRemove
);
2857 m_ptCaretPos
= m_ptSelectionStartPos
;
2861 EnsureCaretVisible();
2865 void CBaseView::PasteText()
2867 if (!OpenClipboard())
2870 CString sClipboardText
;
2871 HGLOBAL hglb
= GetClipboardData(CF_TEXT
);
2874 LPCSTR lpstr
= (LPCSTR
)GlobalLock(hglb
);
2875 sClipboardText
= CString(lpstr
);
2878 hglb
= GetClipboardData(CF_UNICODETEXT
);
2881 LPCTSTR lpstr
= (LPCTSTR
)GlobalLock(hglb
);
2882 sClipboardText
= lpstr
;
2887 if (sClipboardText
.IsEmpty())
2890 sClipboardText
.Replace(_T("\r\n"), _T("\r"));
2891 sClipboardText
.Replace('\n', '\r');
2892 // We want to undo the insertion in a single step.
2893 CUndo::GetInstance().BeginGrouping();
2894 // use the easy way to insert text:
2895 // insert char by char, using the OnChar() method
2896 for (int i
=0; i
<sClipboardText
.GetLength(); ++i
)
2898 OnChar(sClipboardText
[i
], 0, 0);
2900 CUndo::GetInstance().EndGrouping();
2903 void CBaseView::OnCaretDown()
2906 m_ptCaretPos
.y
= min(m_ptCaretPos
.y
, GetLineCount()-1);
2907 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2908 if (GetKeyState(VK_SHIFT
)&0x8000)
2913 EnsureCaretVisible();
2914 ShowDiffLines(m_ptCaretPos
.y
);
2917 bool CBaseView::MoveCaretLeft()
2919 if (m_ptCaretPos
.x
== 0)
2921 if (m_ptCaretPos
.y
> 0)
2924 m_ptCaretPos
.x
= GetLineLength(m_ptCaretPos
.y
);
2936 bool CBaseView::MoveCaretRight()
2938 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
2940 if (m_ptCaretPos
.y
< (GetLineCount() - 1))
2955 void CBaseView::UpdateGoalPos()
2957 m_nCaretGoalPos
= CalculateActualOffset(m_ptCaretPos
.y
, m_ptCaretPos
.x
);
2960 void CBaseView::OnCaretLeft()
2963 if (GetKeyState(VK_SHIFT
)&0x8000)
2967 EnsureCaretVisible();
2971 void CBaseView::OnCaretRight()
2974 if (GetKeyState(VK_SHIFT
)&0x8000)
2978 EnsureCaretVisible();
2982 void CBaseView::OnCaretUp()
2985 m_ptCaretPos
.y
= max(0, m_ptCaretPos
.y
);
2986 m_ptCaretPos
.x
= CalculateCharIndex(m_ptCaretPos
.y
, m_nCaretGoalPos
);
2987 if (GetKeyState(VK_SHIFT
)&0x8000)
2992 EnsureCaretVisible();
2993 ShowDiffLines(m_ptCaretPos
.y
);
2996 bool CBaseView::IsWordSeparator(wchar_t ch
) const
2998 return ch
== ' ' || ch
== '\t' || (m_sWordSeparators
.Find(ch
) >= 0);
3001 bool CBaseView::IsCaretAtWordBoundary() const
3003 LPCTSTR line
= GetLineChars(m_ptCaretPos
.y
);
3005 return false; // no boundary at the empty lines
3006 if (m_ptCaretPos
.x
== 0)
3007 return !IsWordSeparator(line
[m_ptCaretPos
.x
]);
3008 if (m_ptCaretPos
.x
>= GetLineLength(m_ptCaretPos
.y
))
3009 return !IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3011 IsWordSeparator(line
[m_ptCaretPos
.x
]) !=
3012 IsWordSeparator(line
[m_ptCaretPos
.x
- 1]);
3015 void CBaseView::OnCaretWordleft()
3017 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3020 if (GetKeyState(VK_SHIFT
)&0x8000)
3024 EnsureCaretVisible();
3028 void CBaseView::OnCaretWordright()
3030 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3033 if (GetKeyState(VK_SHIFT
)&0x8000)
3037 EnsureCaretVisible();
3041 void CBaseView::ClearCurrentSelection()
3043 m_ptSelectionStartPos
= m_ptCaretPos
;
3044 m_ptSelectionEndPos
= m_ptCaretPos
;
3045 m_ptSelectionOrigin
= m_ptCaretPos
;
3046 m_nSelBlockStart
= -1;
3047 m_nSelBlockEnd
= -1;
3051 void CBaseView::ClearSelection()
3054 m_pwndLeft
->ClearCurrentSelection();
3056 m_pwndRight
->ClearCurrentSelection();
3058 m_pwndBottom
->ClearCurrentSelection();
3061 void CBaseView::AdjustSelection()
3063 if ((m_ptCaretPos
.y
< m_ptSelectionOrigin
.y
) ||
3064 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
<= m_ptSelectionOrigin
.x
))
3066 m_ptSelectionStartPos
= m_ptCaretPos
;
3067 m_ptSelectionEndPos
= m_ptSelectionOrigin
;
3070 if ((m_ptCaretPos
.y
> m_ptSelectionOrigin
.y
) ||
3071 (m_ptCaretPos
.y
== m_ptSelectionOrigin
.y
&& m_ptCaretPos
.x
>= m_ptSelectionOrigin
.x
))
3073 m_ptSelectionStartPos
= m_ptSelectionOrigin
;
3074 m_ptSelectionEndPos
= m_ptCaretPos
;
3077 SetupSelection(min(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
), max(m_ptSelectionStartPos
.y
, m_ptSelectionEndPos
.y
));
3082 void CBaseView::OnEditCut()
3084 if (!m_bCaretHidden
)
3087 RemoveSelectedText();
3091 void CBaseView::OnEditPaste()
3093 if (!m_bCaretHidden
)