optimize TGitCache for CLI operations
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob689c7e48bf90c83a7da2ed43dfbb33725cd76280
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.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
29 #include <deque>
31 #ifdef _DEBUG
32 #define new DEBUG_NEW
33 #endif
35 #define MARGINWIDTH 20
36 #define HEADERHEIGHT 10
38 #define MAXFONTS 8
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;
59 m_pViewData = NULL;
60 m_pOtherViewData = NULL;
61 m_nLineHeight = -1;
62 m_nCharWidth = -1;
63 m_nScreenChars = -1;
64 m_nMaxLineLength = -1;
65 m_nScreenLines = -1;
66 m_nTopLine = 0;
67 m_nOffsetChar = 0;
68 m_nDigits = 0;
69 m_nMouseLine = -1;
70 m_bMouseWithin = FALSE;
71 m_bIsHidden = FALSE;
72 lineendings = EOL_AUTOLINE;
73 m_bCaretHidden = true;
74 m_ptCaretPos.x = 0;
75 m_ptCaretPos.y = 0;
76 m_nCaretGoalPos = 0;
77 m_ptSelectionStartPos = m_ptCaretPos;
78 m_ptSelectionEndPos = m_ptCaretPos;
79 m_ptSelectionOrigin = m_ptCaretPos;
80 m_bFocused = FALSE;
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;
93 m_nSelBlockEnd = -1;
94 m_bModified = FALSE;
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++)
100 m_apFonts[i] = NULL;
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);
125 EnableToolTips();
128 CBaseView::~CBaseView()
130 if (m_pCacheBitmap)
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();
140 delete m_apFonts[i];
142 m_apFonts[i] = NULL;
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)
157 ON_WM_VSCROLL()
158 ON_WM_HSCROLL()
159 ON_WM_ERASEBKGND()
160 ON_WM_CREATE()
161 ON_WM_DESTROY()
162 ON_WM_SIZE()
163 ON_WM_MOUSEWHEEL()
164 ON_WM_SETCURSOR()
165 ON_WM_KILLFOCUS()
166 ON_WM_SETFOCUS()
167 ON_WM_CONTEXTMENU()
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)
172 ON_WM_KEYDOWN()
173 ON_WM_LBUTTONDOWN()
174 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
175 ON_WM_MOUSEMOVE()
176 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
177 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
178 ON_WM_CHAR()
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)
187 ON_WM_MOUSELEAVE()
188 ON_WM_TIMER()
189 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
190 END_MESSAGE_MAP()
193 void CBaseView::DocumentUpdated()
195 if (m_pCacheBitmap != NULL)
197 m_pCacheBitmap->DeleteObject();
198 delete m_pCacheBitmap;
199 m_pCacheBitmap = NULL;
201 m_nLineHeight = -1;
202 m_nCharWidth = -1;
203 m_nScreenChars = -1;
204 m_nMaxLineLength = -1;
205 m_nScreenLines = -1;
206 m_nTopLine = 0;
207 m_bModified = FALSE;
208 m_bOtherDiffChecked = false;
209 m_nDigits = 0;
210 m_nMouseLine = -1;
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();
225 delete m_apFonts[i];
227 m_apFonts[i] = NULL;
229 m_nSelBlockStart = -1;
230 m_nSelBlockEnd = -1;
231 RecalcVertScrollBar();
232 RecalcHorzScrollBar();
233 UpdateStatusBar();
234 Invalidate();
237 void CBaseView::UpdateStatusBar()
239 int nRemovedLines = 0;
240 int nAddedLines = 0;
241 int nConflictedLines = 0;
243 if (m_pViewData)
245 for (int i=0; i<m_pViewData->GetCount(); i++)
247 DiffStates state = m_pViewData->GetState(i);
248 switch (state)
250 case DIFFSTATE_ADDED:
251 case DIFFSTATE_IDENTICALADDED:
252 case DIFFSTATE_THEIRSADDED:
253 case DIFFSTATE_YOURSADDED:
254 case DIFFSTATE_CONFLICTADDED:
255 nAddedLines++;
256 break;
257 case DIFFSTATE_IDENTICALREMOVED:
258 case DIFFSTATE_REMOVED:
259 case DIFFSTATE_THEIRSREMOVED:
260 case DIFFSTATE_YOURSREMOVED:
261 nRemovedLines++;
262 break;
263 case DIFFSTATE_CONFLICTED:
264 case DIFFSTATE_CONFLICTED_IGNORED:
265 nConflictedLines++;
266 break;
271 CString sBarText;
272 CString sTemp;
274 switch (texttype)
276 case CFileTextLines::ASCII:
277 sBarText = _T("ASCII ");
278 break;
279 case CFileTextLines::BINARY:
280 sBarText = _T("BINARY ");
281 break;
282 case CFileTextLines::UNICODE_LE:
283 sBarText = _T("UTF-16LE ");
284 break;
285 case CFileTextLines::UTF8:
286 sBarText = _T("UTF8 ");
287 break;
288 case CFileTextLines::UTF8BOM:
289 sBarText = _T("UTF8 BOM ");
290 break;
293 switch(lineendings)
295 case EOL_LF:
296 sBarText += _T("LF ");
297 break;
298 case EOL_CRLF:
299 sBarText += _T("CRLF ");
300 break;
301 case EOL_LFCR:
302 sBarText += _T("LFCR ");
303 break;
304 case EOL_CR:
305 sBarText += _T("CR ");
306 break;
309 if (sBarText.IsEmpty())
310 sBarText += _T(" / ");
312 if (nRemovedLines)
314 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
315 if (!sBarText.IsEmpty())
316 sBarText += _T(" / ");
317 sBarText += sTemp;
319 if (nAddedLines)
321 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
322 if (!sBarText.IsEmpty())
323 sBarText += _T(" / ");
324 sBarText += sTemp;
326 if (nConflictedLines)
328 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
329 if (!sBarText.IsEmpty())
330 sBarText += _T(" / ");
331 sBarText += sTemp;
333 if (m_pwndStatusBar)
335 UINT nID;
336 UINT nStyle;
337 int cxWidth;
338 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
339 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
341 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
343 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
345 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
346 sBarText = sTemp+sBarText;
348 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
350 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
351 sBarText = sTemp+sBarText;
353 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
354 //calculate the width of the text
355 CDC * pDC = m_pwndStatusBar->GetDC();
356 if (pDC)
358 CSize size = pDC->GetTextExtent(sBarText);
359 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
360 ReleaseDC(pDC);
362 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
366 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
368 if (!CView::PreCreateWindow(cs))
369 return FALSE;
371 cs.dwExStyle |= WS_EX_CLIENTEDGE;
372 cs.style &= ~WS_BORDER;
373 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
374 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
376 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
377 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
379 // View must always create its own scrollbars,
380 // if only it's not used within splitter
381 cs.style |= (WS_HSCROLL | WS_VSCROLL);
383 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
384 return TRUE;
387 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/, BOOL bStrikeOut /*= FALSE*/)
389 int nIndex = 0;
390 if (bBold)
391 nIndex |= 1;
392 if (bItalic)
393 nIndex |= 2;
394 if (bStrikeOut)
395 nIndex |= 4;
396 if (m_apFonts[nIndex] == NULL)
398 m_apFonts[nIndex] = new CFont;
399 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
400 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
401 m_lfBaseFont.lfItalic = (BYTE) bItalic;
402 m_lfBaseFont.lfStrikeOut = (BYTE) bStrikeOut;
403 if (bStrikeOut)
404 m_lfBaseFont.lfStrikeOut = (BYTE)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE);
405 CDC * pDC = GetDC();
406 if (pDC)
408 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
409 ReleaseDC(pDC);
411 _tcsncpy_s(m_lfBaseFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
412 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
414 delete m_apFonts[nIndex];
415 m_apFonts[nIndex] = NULL;
416 return CView::GetFont();
419 return m_apFonts[nIndex];
422 void CBaseView::CalcLineCharDim()
424 CDC *pDC = GetDC();
425 CFont *pOldFont = pDC->SelectObject(GetFont());
426 CSize szCharExt = pDC->GetTextExtent(_T("X"));
427 m_nLineHeight = szCharExt.cy;
428 if (m_nLineHeight <= 0)
429 m_nLineHeight = -1;
430 m_nCharWidth = szCharExt.cx;
431 if (m_nCharWidth <= 0)
432 m_nCharWidth = -1;
433 pDC->SelectObject(pOldFont);
434 ReleaseDC(pDC);
437 int CBaseView::GetScreenChars()
439 if (m_nScreenChars == -1)
441 CRect rect;
442 GetClientRect(&rect);
443 m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
445 return m_nScreenChars;
448 int CBaseView::GetAllMinScreenChars() const
450 int nChars = 0;
451 if (IsLeftViewGood())
452 nChars = m_pwndLeft->GetScreenChars();
453 if (IsRightViewGood())
454 nChars = (nChars < m_pwndRight->GetScreenChars() ? nChars : m_pwndRight->GetScreenChars());
455 if (IsBottomViewGood())
456 nChars = (nChars < m_pwndBottom->GetScreenChars() ? nChars : m_pwndBottom->GetScreenChars());
457 return nChars;
460 int CBaseView::GetAllMaxLineLength() const
462 int nLength = 0;
463 if (IsLeftViewGood())
464 nLength = m_pwndLeft->GetMaxLineLength();
465 if (IsRightViewGood())
466 nLength = (nLength > m_pwndRight->GetMaxLineLength() ? nLength : m_pwndRight->GetMaxLineLength());
467 if (IsBottomViewGood())
468 nLength = (nLength > m_pwndBottom->GetMaxLineLength() ? nLength : m_pwndBottom->GetMaxLineLength());
469 return nLength;
472 int CBaseView::GetLineHeight()
474 if (m_nLineHeight == -1)
475 CalcLineCharDim();
476 if (m_nLineHeight <= 0)
477 return 1;
478 return m_nLineHeight;
481 int CBaseView::GetCharWidth()
483 if (m_nCharWidth == -1)
484 CalcLineCharDim();
485 if (m_nCharWidth <= 0)
486 return 1;
487 return m_nCharWidth;
490 int CBaseView::GetMaxLineLength()
492 if (m_nMaxLineLength == -1)
494 m_nMaxLineLength = 0;
495 int nLineCount = GetLineCount();
496 for (int i=0; i<nLineCount; i++)
498 int nActualLength = GetLineActualLength(i);
499 if (m_nMaxLineLength < nActualLength)
500 m_nMaxLineLength = nActualLength;
503 return m_nMaxLineLength;
506 int CBaseView::GetLineActualLength(int index) const
508 if (m_pViewData == NULL)
509 return 0;
511 return CalculateActualOffset(index, GetLineLength(index));
514 int CBaseView::GetLineLength(int index) const
516 if (m_pViewData == NULL)
517 return 0;
518 if (m_pViewData->GetCount() == 0)
519 return 0;
520 int nLineLength = m_pViewData->GetLine(index).GetLength();
521 ASSERT(nLineLength >= 0);
522 return nLineLength;
525 int CBaseView::GetLineCount() const
527 if (m_pViewData == NULL)
528 return 1;
529 int nLineCount = m_pViewData->GetCount();
530 ASSERT(nLineCount >= 0);
531 return nLineCount;
534 LPCTSTR CBaseView::GetLineChars(int index) const
536 if (m_pViewData == NULL)
537 return 0;
538 if (m_pViewData->GetCount() == 0)
539 return 0;
540 return m_pViewData->GetLine(index);
543 void CBaseView::CheckOtherView()
545 if (m_bOtherDiffChecked)
546 return;
547 // find out what the 'other' file is
548 m_pOtherViewData = NULL;
549 if (this == m_pwndLeft && IsRightViewGood())
550 m_pOtherViewData = m_pwndRight->m_pViewData;
552 if (this == m_pwndRight && IsLeftViewGood())
553 m_pOtherViewData = m_pwndLeft->m_pViewData;
555 m_bOtherDiffChecked = true;
558 CString CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex)
560 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
561 ASSERT(viewData);
563 DiffStates origstate = viewData->GetState(nLineIndex);
565 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
566 int nStartBlock = nLineIndex;
567 int nEndBlock = nLineIndex;
568 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
570 DiffStates state = viewData->GetState(nStartBlock - 1);
571 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
572 origstate = state;
573 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
574 nStartBlock--;
575 else
576 break;
578 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
580 DiffStates state = viewData->GetState(nEndBlock + 1);
581 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
582 origstate = state;
583 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
584 nEndBlock++;
585 else
586 break;
589 CString block;
590 for (int i = nStartBlock; i <= nEndBlock; ++i)
591 block += viewData->GetLine(i);
592 return block;
595 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
597 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
598 CheckOtherView();
599 if (!m_pOtherViewData)
600 return false;
601 if (
602 (m_pViewData->GetState(nLineIndex) == DIFFSTATE_NORMAL) &&
603 (m_pOtherViewData->GetLine(nLineIndex) == m_pViewData->GetLine(nLineIndex))
605 return false;
607 CString mine = GetWhitespaceBlock(m_pViewData, nLineIndex);
608 CString other = GetWhitespaceBlock(m_pOtherViewData, min(nLineIndex, m_pOtherViewData->GetCount() - 1));
609 bIdentical = mine == other;
611 mine.Remove(' ');
612 mine.Remove('\t');
613 mine.Remove('\r');
614 mine.Remove('\n');
615 other.Remove(' ');
616 other.Remove('\t');
617 other.Remove('\r');
618 other.Remove('\n');
620 return (mine == other) && (!mine.IsEmpty());
623 int CBaseView::GetLineNumber(int index) const
625 if (m_pViewData == NULL)
626 return -1;
627 if (m_pViewData->GetLineNumber(index)==DIFF_EMPTYLINENUMBER)
628 return -1;
629 return m_pViewData->GetLineNumber(index);
632 int CBaseView::GetScreenLines()
634 if (m_nScreenLines == -1)
636 SCROLLBARINFO sbi;
637 sbi.cbSize = sizeof(sbi);
638 GetScrollBarInfo(OBJID_HSCROLL, &sbi);
639 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
641 CRect rect;
642 GetClientRect(&rect);
643 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
645 return m_nScreenLines;
648 int CBaseView::GetAllMinScreenLines() const
650 int nLines = 0;
651 if (IsLeftViewGood())
652 nLines = m_pwndLeft->GetScreenLines();
653 if (IsRightViewGood())
654 nLines = (nLines < m_pwndRight->GetScreenLines() ? nLines : m_pwndRight->GetScreenLines());
655 if (IsBottomViewGood())
656 nLines = (nLines < m_pwndBottom->GetScreenLines() ? nLines : m_pwndBottom->GetScreenLines());
657 return nLines;
660 int CBaseView::GetAllLineCount() const
662 int nLines = 0;
663 if (IsLeftViewGood())
664 nLines = m_pwndLeft->GetLineCount();
665 if (IsRightViewGood())
666 nLines = (nLines > m_pwndRight->GetLineCount() ? nLines : m_pwndRight->GetLineCount());
667 if (IsBottomViewGood())
668 nLines = (nLines > m_pwndBottom->GetLineCount() ? nLines : m_pwndBottom->GetLineCount());
669 return nLines;
672 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
674 if (IsLeftViewGood())
675 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
676 if (IsRightViewGood())
677 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
678 if (IsBottomViewGood())
679 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
682 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
684 SCROLLINFO si;
685 si.cbSize = sizeof(si);
686 if (bPositionOnly)
688 si.fMask = SIF_POS;
689 si.nPos = m_nTopLine;
691 else
693 EnableScrollBarCtrl(SB_VERT, TRUE);
694 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
696 m_nTopLine = 0;
697 Invalidate();
699 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
700 si.nMin = 0;
701 si.nMax = GetAllLineCount();
702 si.nPage = GetAllMinScreenLines();
703 si.nPos = m_nTopLine;
705 VERIFY(SetScrollInfo(SB_VERT, &si));
708 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
710 CView::OnVScroll(nSBCode, nPos, pScrollBar);
711 if (m_pwndLeft)
712 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
713 if (m_pwndRight)
714 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
715 if (m_pwndBottom)
716 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
717 if (m_pwndLocator)
718 m_pwndLocator->Invalidate();
721 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
723 // Note we cannot use nPos because of its 16-bit nature
724 SCROLLINFO si;
725 si.cbSize = sizeof(si);
726 si.fMask = SIF_ALL;
727 VERIFY(master->GetScrollInfo(SB_VERT, &si));
729 int nPageLines = GetScreenLines();
730 int nLineCount = GetLineCount();
732 RECT thumbrect;
733 POINT thumbpoint;
734 int nNewTopLine;
736 static LONG textwidth = 0;
737 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
738 switch (nSBCode)
740 case SB_TOP:
741 nNewTopLine = 0;
742 break;
743 case SB_BOTTOM:
744 nNewTopLine = nLineCount - nPageLines + 1;
745 break;
746 case SB_LINEUP:
747 nNewTopLine = m_nTopLine - 1;
748 break;
749 case SB_LINEDOWN:
750 nNewTopLine = m_nTopLine + 1;
751 break;
752 case SB_PAGEUP:
753 nNewTopLine = m_nTopLine - si.nPage + 1;
754 break;
755 case SB_PAGEDOWN:
756 nNewTopLine = m_nTopLine + si.nPage - 1;
757 break;
758 case SB_THUMBPOSITION:
759 m_ScrollTool.Clear();
760 nNewTopLine = si.nTrackPos;
761 textwidth = 0;
762 break;
763 case SB_THUMBTRACK:
764 nNewTopLine = si.nTrackPos;
765 if (GetFocus() == this)
767 GetClientRect(&thumbrect);
768 ClientToScreen(&thumbrect);
769 thumbpoint.x = thumbrect.right;
770 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
771 m_ScrollTool.Init(&thumbpoint);
772 if (textwidth == 0)
774 CString sTemp = sFormat;
775 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
776 textwidth = m_ScrollTool.GetTextWidth(sTemp);
778 thumbpoint.x -= textwidth;
779 int line = GetLineNumber(nNewTopLine);
780 if (line >= 0)
781 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
782 else
783 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
785 break;
786 default:
787 return;
790 if (nNewTopLine < 0)
791 nNewTopLine = 0;
792 if (nNewTopLine >= nLineCount)
793 nNewTopLine = nLineCount - 1;
794 ScrollToLine(nNewTopLine);
797 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
799 if (IsLeftViewGood())
800 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
801 if (IsRightViewGood())
802 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
803 if (IsBottomViewGood())
804 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
807 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
809 SCROLLINFO si;
810 si.cbSize = sizeof(si);
811 if (bPositionOnly)
813 si.fMask = SIF_POS;
814 si.nPos = m_nOffsetChar;
816 else
818 EnableScrollBarCtrl(SB_HORZ, TRUE);
819 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar > 0)
821 m_nOffsetChar = 0;
822 Invalidate();
824 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
825 si.nMin = 0;
826 si.nMax = GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
827 si.nPage = GetAllMinScreenChars();
828 si.nPos = m_nOffsetChar;
830 VERIFY(SetScrollInfo(SB_HORZ, &si));
833 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
835 CView::OnHScroll(nSBCode, nPos, pScrollBar);
836 if (m_pwndLeft)
837 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
838 if (m_pwndRight)
839 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
840 if (m_pwndBottom)
841 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
842 if (m_pwndLocator)
843 m_pwndLocator->Invalidate();
846 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
848 SCROLLINFO si;
849 si.cbSize = sizeof(si);
850 si.fMask = SIF_ALL;
851 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
853 int nPageChars = GetScreenChars();
854 int nMaxLineLength = GetMaxLineLength();
856 int nNewOffset;
857 switch (nSBCode)
859 case SB_LEFT:
860 nNewOffset = 0;
861 break;
862 case SB_BOTTOM:
863 nNewOffset = nMaxLineLength - nPageChars + 1;
864 break;
865 case SB_LINEUP:
866 nNewOffset = m_nOffsetChar - 1;
867 break;
868 case SB_LINEDOWN:
869 nNewOffset = m_nOffsetChar + 1;
870 break;
871 case SB_PAGEUP:
872 nNewOffset = m_nOffsetChar - si.nPage + 1;
873 break;
874 case SB_PAGEDOWN:
875 nNewOffset = m_nOffsetChar + si.nPage - 1;
876 break;
877 case SB_THUMBPOSITION:
878 case SB_THUMBTRACK:
879 nNewOffset = si.nTrackPos;
880 break;
881 default:
882 return;
885 if (nNewOffset >= nMaxLineLength)
886 nNewOffset = nMaxLineLength - 1;
887 if (nNewOffset < 0)
888 nNewOffset = 0;
889 ScrollToChar(nNewOffset, TRUE);
892 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
894 if (m_nOffsetChar != nNewOffsetChar)
896 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
897 m_nOffsetChar = nNewOffsetChar;
898 CRect rcScroll;
899 GetClientRect(&rcScroll);
900 rcScroll.left += GetMarginWidth();
901 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
902 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
903 // update the view header
904 rcScroll.left = 0;
905 rcScroll.top = 0;
906 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
907 InvalidateRect(&rcScroll, FALSE);
908 UpdateWindow();
909 if (bTrackScrollBar)
910 RecalcHorzScrollBar(TRUE);
911 UpdateCaret();
915 void CBaseView::ScrollSide(int delta)
917 int nNewOffset = m_nOffsetChar;
918 nNewOffset += delta;
919 int nMaxLineLength = GetMaxLineLength();
920 if (nNewOffset >= nMaxLineLength)
921 nNewOffset = nMaxLineLength - 1;
922 if (nNewOffset < 0)
923 nNewOffset = 0;
924 ScrollToChar(nNewOffset, TRUE);
925 if (m_pwndLineDiffBar)
926 m_pwndLineDiffBar->Invalidate();
927 UpdateCaret();
930 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
932 if (m_nTopLine != nNewTopLine)
934 if (nNewTopLine < 0)
935 nNewTopLine = 0;
936 int nScrollLines = m_nTopLine - nNewTopLine;
937 m_nTopLine = nNewTopLine;
938 CRect rcScroll;
939 GetClientRect(&rcScroll);
940 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
941 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
942 UpdateWindow();
943 if (bTrackScrollBar)
944 RecalcVertScrollBar(TRUE);
945 UpdateCaret();
950 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
952 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
954 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
956 DiffStates state = m_pViewData->GetState(nLineIndex);
957 HICON icon = NULL;
958 switch (state)
960 case DIFFSTATE_ADDED:
961 case DIFFSTATE_THEIRSADDED:
962 case DIFFSTATE_YOURSADDED:
963 case DIFFSTATE_IDENTICALADDED:
964 case DIFFSTATE_CONFLICTADDED:
965 icon = m_hAddedIcon;
966 break;
967 case DIFFSTATE_REMOVED:
968 case DIFFSTATE_THEIRSREMOVED:
969 case DIFFSTATE_YOURSREMOVED:
970 case DIFFSTATE_IDENTICALREMOVED:
971 icon = m_hRemovedIcon;
972 break;
973 case DIFFSTATE_CONFLICTED:
974 icon = m_hConflictedIcon;
975 break;
976 case DIFFSTATE_CONFLICTED_IGNORED:
977 icon = m_hConflictedIgnoredIcon;
978 break;
979 case DIFFSTATE_EDITED:
980 icon = m_hEditedIcon;
981 break;
982 default:
983 break;
985 bool bIdentical = false;
986 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
988 if (bIdentical)
989 icon = m_hEqualIcon;
990 else
991 icon = m_hWhitespaceBlockIcon;
994 if (icon)
996 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
998 if ((m_bViewLinenumbers)&&(m_nDigits))
1000 int nLineNumber = GetLineNumber(nLineIndex);
1001 if (nLineNumber >= 0)
1003 CString sLinenumberFormat;
1004 CString sLinenumber;
1005 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1006 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1007 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1008 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1010 pdc->SelectObject(GetFont());
1011 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1017 int CBaseView::GetMarginWidth()
1019 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1021 int nWidth = GetCharWidth();
1022 if (m_nDigits <= 0)
1024 int nLength = (int)m_pViewData->GetCount();
1025 // find out how many digits are needed to show the highest line number
1026 int nDigits = 1;
1027 while (nLength / 10)
1029 nDigits++;
1030 nLength /= 10;
1032 m_nDigits = nDigits;
1034 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1036 return MARGINWIDTH;
1039 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1041 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1042 COLORREF crBk, crFg;
1043 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1044 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1045 if (IsBottomViewGood())
1047 pdc->SetBkColor(crBk);
1049 else
1052 if (this == m_pwndRight)
1054 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED, crBk, crFg);
1055 pdc->SetBkColor(crBk);
1057 else
1059 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED, crBk, crFg);
1060 pdc->SetBkColor(crBk);
1063 pdc->FillSolidRect(textrect, crBk);
1065 pdc->SetTextColor(crFg);
1067 pdc->SelectObject(GetFont(FALSE, TRUE, FALSE));
1068 if (IsModified())
1070 if (m_sWindowName.Left(2).Compare(_T("* "))!=0)
1071 m_sWindowName = _T("* ") + m_sWindowName;
1073 else
1075 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
1076 m_sWindowName = m_sWindowName.Mid(2);
1078 CString sViewTitle = m_sWindowName;
1079 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1080 if (nStringLength > rect.Width())
1082 int offset = min(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1084 sViewTitle = m_sWindowName.Mid(offset);
1086 pdc->ExtTextOut(max(rect.left + (rect.Width()-nStringLength)/2, 1),
1087 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1088 if (this->GetFocus() == this)
1089 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1090 else
1091 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1094 void CBaseView::OnDraw(CDC * pDC)
1096 CRect rcClient;
1097 GetClientRect(rcClient);
1099 int nLineCount = GetLineCount();
1100 int nLineHeight = GetLineHeight();
1102 CDC cacheDC;
1103 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1104 if (m_pCacheBitmap == NULL)
1106 m_pCacheBitmap = new CBitmap;
1107 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1109 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1111 DrawHeader(pDC, rcClient);
1113 CRect rcLine;
1114 rcLine = rcClient;
1115 rcLine.top += nLineHeight+HEADERHEIGHT;
1116 rcLine.bottom = rcLine.top + nLineHeight;
1117 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1118 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1120 int nCurrentLine = m_nTopLine;
1121 while (rcLine.top < rcClient.bottom)
1123 if (nCurrentLine < nLineCount)
1125 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1126 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1128 else
1130 DrawMargin(&cacheDC, rcCacheMargin, -1);
1131 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1134 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1136 nCurrentLine ++;
1137 rcLine.OffsetRect(0, nLineHeight);
1140 cacheDC.SelectObject(pOldBitmap);
1141 cacheDC.DeleteDC();
1144 BOOL CBaseView::IsLineRemoved(int nLineIndex)
1146 DiffStates state = DIFFSTATE_UNKNOWN;
1147 if (m_pViewData)
1148 state = m_pViewData->GetState(nLineIndex);
1149 BOOL ret = FALSE;
1150 switch (state)
1152 case DIFFSTATE_REMOVED:
1153 case DIFFSTATE_THEIRSREMOVED:
1154 case DIFFSTATE_YOURSREMOVED:
1155 case DIFFSTATE_IDENTICALREMOVED:
1156 ret = TRUE;
1157 break;
1158 default:
1159 ret = FALSE;
1160 break;
1162 return ret;
1165 bool CBaseView::IsLineConflicted(int nLineIndex)
1167 DiffStates state = DIFFSTATE_UNKNOWN;
1168 if (m_pViewData)
1169 state = m_pViewData->GetState(nLineIndex);
1170 bool ret = false;
1171 switch (state)
1173 case DIFFSTATE_CONFLICTED:
1174 case DIFFSTATE_CONFLICTED_IGNORED:
1175 case DIFFSTATE_CONFLICTEMPTY:
1176 case DIFFSTATE_CONFLICTADDED:
1177 ret = true;
1178 break;
1179 default:
1180 ret = false;
1181 break;
1183 return ret;
1186 COLORREF CBaseView::IntenseColor(long scale, COLORREF col)
1188 // if the color is already dark (gray scale below 127),
1189 // then lighten the color by 'scale', otherwise darken it
1190 int Gray = (((int)GetRValue(col)) + GetGValue(col) + GetBValue(col))/3;
1191 if (Gray > 127)
1193 long red = MulDiv(GetRValue(col),(255-scale),255);
1194 long green = MulDiv(GetGValue(col),(255-scale),255);
1195 long blue = MulDiv(GetBValue(col),(255-scale),255);
1197 return RGB(red, green, blue);
1199 long R = MulDiv(255-GetRValue(col),scale,255)+GetRValue(col);
1200 long G = MulDiv(255-GetGValue(col),scale,255)+GetGValue(col);
1201 long B = MulDiv(255-GetBValue(col),scale,255)+GetBValue(col);
1203 return RGB(R, G, B);
1206 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1208 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1211 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1213 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < m_pViewData->GetCount())))
1214 return;
1216 EOL ending = m_pViewData->GetLineEnding(nLineIndex);
1217 if (m_bIconLFs)
1219 HICON hEndingIcon = NULL;
1220 switch (ending)
1222 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1223 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1224 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1225 default: return;
1227 if (origin.x < (rc.left-GetCharWidth()))
1228 return;
1229 // If EOL style has changed, color end-of-line markers as inline differences.
1231 m_bShowInlineDiff && m_pOtherViewData &&
1232 (nLineIndex < m_pOtherViewData->GetCount()) &&
1233 (ending != EOL_NOENDING) &&
1234 (ending != m_pOtherViewData->GetLineEnding(nLineIndex) &&
1235 (m_pOtherViewData->GetLineEnding(nLineIndex) != EOL_NOENDING))
1238 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1241 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1243 else
1245 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1246 CPen * oldpen = pDC->SelectObject(&pen);
1247 int yMiddle = origin.y + rc.Height()/2;
1248 int xMiddle = origin.x+GetCharWidth()/2;
1249 switch (ending)
1251 case EOL_CR:
1252 // arrow from right to left
1253 pDC->MoveTo(origin.x+GetCharWidth(), yMiddle);
1254 pDC->LineTo(origin.x, yMiddle);
1255 pDC->LineTo(origin.x+4, yMiddle+4);
1256 pDC->MoveTo(origin.x, yMiddle);
1257 pDC->LineTo(origin.x+4, yMiddle-4);
1258 break;
1259 case EOL_CRLF:
1260 // arrow from top to middle+2, then left
1261 pDC->MoveTo(origin.x+GetCharWidth(), rc.top);
1262 pDC->LineTo(origin.x+GetCharWidth(), yMiddle);
1263 pDC->LineTo(origin.x, yMiddle);
1264 pDC->LineTo(origin.x+4, yMiddle+4);
1265 pDC->MoveTo(origin.x, yMiddle);
1266 pDC->LineTo(origin.x+4, yMiddle-4);
1267 break;
1268 case EOL_LF:
1269 // arrow from top to bottom
1270 pDC->MoveTo(xMiddle, rc.top);
1271 pDC->LineTo(xMiddle, rc.bottom-1);
1272 pDC->LineTo(xMiddle+4, rc.bottom-5);
1273 pDC->MoveTo(xMiddle, rc.bottom-1);
1274 pDC->LineTo(xMiddle-4, rc.bottom-5);
1275 break;
1277 pDC->SelectObject(oldpen);
1281 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1283 const int THICKNESS = 2;
1284 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1285 if ((nLineIndex == m_nSelBlockStart) && m_bShowSelection)
1287 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1289 if ((nLineIndex == m_nSelBlockEnd) && m_bShowSelection)
1291 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1295 void CBaseView::DrawText(
1296 CDC * pDC, const CRect &rc, LPCTSTR text, int textlength, int nLineIndex, POINT coords, bool bModified, bool bInlineDiff)
1298 ASSERT(m_pViewData && (nLineIndex < m_pViewData->GetCount()));
1299 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1301 // first suppose the whole line is selected
1302 int selectedStart = 0, selectedEnd = textlength;
1304 if ((m_ptSelectionStartPos.y > nLineIndex) || (m_ptSelectionEndPos.y < nLineIndex)
1305 || ! m_bShowSelection)
1307 // this line has no selected text
1308 selectedStart = textlength;
1310 else if ((m_ptSelectionStartPos.y == nLineIndex) || (m_ptSelectionEndPos.y == nLineIndex))
1312 // the line is partially selected
1313 int xoffs = m_nOffsetChar + (coords.x - GetMarginWidth()) / GetCharWidth();
1314 if (m_ptSelectionStartPos.y == nLineIndex)
1316 // the first line of selection
1317 int nSelectionStartOffset = CalculateActualOffset(m_ptSelectionStartPos.y, m_ptSelectionStartPos.x);
1318 selectedStart = max(min(nSelectionStartOffset - xoffs, textlength), 0);
1321 if (m_ptSelectionEndPos.y == nLineIndex)
1323 // the last line of selection
1324 int nSelectionEndOffset = CalculateActualOffset(m_ptSelectionEndPos.y, m_ptSelectionEndPos.x);
1325 selectedEnd = max(min(nSelectionEndOffset - xoffs, textlength), 0);
1329 COLORREF crBkgnd, crText;
1330 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1331 if (bModified || (diffState == DIFFSTATE_EDITED))
1332 crBkgnd = m_ModifiedBk;
1333 if (bInlineDiff)
1334 crBkgnd = InlineDiffColor(nLineIndex);
1336 pDC->SetBkColor(crBkgnd);
1337 pDC->SetTextColor(crText);
1338 if (selectedStart>=0)
1339 VERIFY(pDC->ExtTextOut(coords.x, coords.y, ETO_CLIPPED, &rc, text, selectedStart, NULL));
1341 long intenseColorScale = m_bFocused ? 70 : 30;
1342 pDC->SetBkColor(IntenseColor(intenseColorScale, crBkgnd));
1343 pDC->SetTextColor(IntenseColor(intenseColorScale, crText));
1344 VERIFY(pDC->ExtTextOut(
1345 coords.x + selectedStart * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1346 text + selectedStart, selectedEnd - selectedStart, NULL));
1348 pDC->SetBkColor(crBkgnd);
1349 pDC->SetTextColor(crText);
1350 if (textlength - selectedEnd >= 0)
1351 VERIFY(pDC->ExtTextOut(
1352 coords.x + selectedEnd * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1353 text + selectedEnd, textlength - selectedEnd, NULL));
1356 bool CBaseView::DrawInlineDiff(CDC *pDC, const CRect &rc, int nLineIndex, const CString &line, CPoint &origin)
1358 if (!m_bShowInlineDiff || line.IsEmpty())
1359 return false;
1360 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
1361 return false;
1363 LPCTSTR pszDiffChars = NULL;
1364 int nDiffLength = 0;
1365 if (m_pOtherViewData)
1367 int index = min(nLineIndex, m_pOtherViewData->GetCount() - 1);
1368 pszDiffChars = m_pOtherViewData->GetLine(index);
1369 nDiffLength = m_pOtherViewData->GetLine(index).GetLength();
1372 if (!pszDiffChars || !*pszDiffChars)
1373 return false;
1375 CString diffline;
1376 ExpandChars(pszDiffChars, 0, nDiffLength, diffline);
1377 svn_diff_t * diff = NULL;
1378 m_svnlinediff.Diff(&diff, line, line.GetLength(), diffline, diffline.GetLength(), m_bInlineWordDiff);
1379 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
1380 return false;
1382 int lineoffset = 0;
1383 std::deque<int> removedPositions;
1384 while (diff)
1386 apr_off_t len = diff->original_length;
1388 CString s;
1389 for (int i = 0; i < len; ++i)
1391 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
1392 lineoffset++;
1394 bool isModified = diff->type == svn_diff__type_diff_modified;
1395 DrawText(pDC, rc, (LPCTSTR)s, s.GetLength(), nLineIndex, origin, true, isModified);
1396 origin.x += pDC->GetTextExtent(s).cx;
1398 if (isModified && (len < diff->modified_length))
1399 removedPositions.push_back(origin.x - 1);
1401 diff = diff->next;
1403 // Draw vertical bars at removed chunks' positions.
1404 for (std::deque<int>::iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
1405 pDC->FillSolidRect(*it, rc.top, 1, rc.Height(), m_InlineRemovedBk);
1406 return true;
1409 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1411 if (nLineIndex >= GetLineCount())
1412 nLineIndex = -1;
1413 ASSERT(nLineIndex >= -1);
1415 if ((nLineIndex == -1) || !m_pViewData)
1417 // Draw line beyond the text
1418 COLORREF crBkgnd, crText;
1419 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1420 pDC->FillSolidRect(rc, crBkgnd);
1421 return;
1424 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1425 COLORREF crBkgnd, crText;
1426 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1428 if (diffState == DIFFSTATE_CONFLICTED)
1430 // conflicted lines are shown without 'text' on them
1431 CRect rect = rc;
1432 pDC->FillSolidRect(rc, crBkgnd);
1433 // now draw some faint text patterns
1434 pDC->SetTextColor(IntenseColor(130, crBkgnd));
1435 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1436 DrawBlockLine(pDC, rc, nLineIndex);
1437 return;
1440 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1441 int nLength = GetLineLength(nLineIndex);
1442 if (nLength == 0)
1444 // Draw the empty line
1445 pDC->FillSolidRect(rc, crBkgnd);
1446 DrawBlockLine(pDC, rc, nLineIndex);
1447 DrawLineEnding(pDC, rc, nLineIndex, origin);
1448 return;
1450 LPCTSTR pszChars = GetLineChars(nLineIndex);
1451 if (pszChars == NULL)
1452 return;
1454 CheckOtherView();
1456 // Draw the line
1458 pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));
1459 CString line;
1460 ExpandChars(pszChars, 0, nLength, line);
1462 int nWidth = rc.right - origin.x;
1463 int savedx = origin.x;
1464 bool bInlineDiffDrawn =
1465 nWidth > 0 && diffState != DIFFSTATE_NORMAL &&
1466 DrawInlineDiff(pDC, rc, nLineIndex, line, origin);
1468 if (!bInlineDiffDrawn)
1470 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);
1471 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);
1474 origin.x = savedx + pDC->GetTextExtent(line).cx;
1476 // draw white space after the end of line
1477 CRect frect = rc;
1478 if (origin.x > frect.left)
1479 frect.left = origin.x;
1480 if (bInlineDiffDrawn)
1481 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1482 if (frect.right > frect.left)
1483 pDC->FillSolidRect(frect, crBkgnd);
1484 // draw the whitespace chars
1485 if (m_bViewWhitespace)
1487 int xpos = 0;
1488 int y = rc.top + (rc.bottom-rc.top)/2;
1490 int nActualOffset = 0;
1491 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1493 if (*pszChars == _T('\t'))
1494 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1495 else
1496 nActualOffset++;
1497 pszChars++;
1499 if (nActualOffset > m_nOffsetChar)
1500 pszChars--;
1502 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1503 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1504 while (*pszChars)
1506 switch (*pszChars)
1508 case _T('\t'):
1510 // draw an arrow
1511 CPen * oldPen = pDC->SelectObject(&pen);
1512 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1513 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1514 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1515 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1516 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1517 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1518 xpos += nSpaces;
1519 pDC->SelectObject(oldPen);
1521 break;
1522 case _T(' '):
1524 // draw a small dot
1525 CPen * oldPen = pDC->SelectObject(&pen2);
1526 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1527 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1528 xpos++;
1529 pDC->SelectObject(oldPen);
1531 break;
1532 default:
1533 xpos++;
1534 break;
1536 pszChars++;
1539 DrawBlockLine(pDC, rc, nLineIndex);
1540 DrawLineEnding(pDC, rc, nLineIndex, origin);
1543 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
1545 if (nCount <= 0)
1547 line = _T("");
1548 return;
1551 int nTabSize = GetTabSize();
1553 int nActualOffset = 0;
1554 for (int i=0; i<nOffset; i++)
1556 if (pszChars[i] == _T('\t'))
1557 nActualOffset += (nTabSize - nActualOffset % nTabSize);
1558 else
1559 nActualOffset ++;
1562 pszChars += nOffset;
1563 int nLength = nCount;
1565 int nTabCount = 0;
1566 for (int i=0; i<nLength; i++)
1568 if (pszChars[i] == _T('\t'))
1569 nTabCount ++;
1572 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1573 int nCurPos = 0;
1574 if (nTabCount > 0 || m_bViewWhitespace)
1576 for (int i=0; i<nLength; i++)
1578 if (pszChars[i] == _T('\t'))
1580 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1581 while (nSpaces > 0)
1583 pszBuf[nCurPos ++] = _T(' ');
1584 nSpaces --;
1587 else
1589 pszBuf[nCurPos] = pszChars[i];
1590 nCurPos ++;
1594 else
1596 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1597 nCurPos = nLength;
1599 pszBuf[nCurPos] = 0;
1600 line.ReleaseBuffer();
1603 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
1605 if ((m_pwndLeft)&&(m_pwndRight))
1607 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1608 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1610 else
1612 if (m_pwndLeft)
1613 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1614 if (m_pwndRight)
1615 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1617 if (m_pwndBottom)
1618 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
1619 if (m_pwndLocator)
1620 m_pwndLocator->Invalidate();
1623 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
1625 //almost the same as ScrollAllToLine, but try to put the line in the
1626 //middle of the view, not on top
1627 int nNewTopLine = nNewLine - GetScreenLines()/2;
1628 if (nNewTopLine < 0)
1629 nNewTopLine = 0;
1630 if (m_pViewData)
1632 if (nNewTopLine >= m_pViewData->GetCount())
1633 nNewTopLine = m_pViewData->GetCount()-1;
1634 if (bAll)
1635 ScrollAllToLine(nNewTopLine);
1636 else
1637 ScrollToLine(nNewTopLine);
1641 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
1643 return TRUE;
1646 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
1648 if (CView::OnCreate(lpCreateStruct) == -1)
1649 return -1;
1651 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
1652 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1653 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1654 m_lfBaseFont.lfHeight = 0;
1655 m_lfBaseFont.lfWeight = FW_NORMAL;
1656 m_lfBaseFont.lfItalic = FALSE;
1657 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
1658 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1659 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1660 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
1661 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
1663 return 0;
1666 void CBaseView::OnDestroy()
1668 CView::OnDestroy();
1669 for (int i=0; i<MAXFONTS; i++)
1671 if (m_apFonts[i] != NULL)
1673 m_apFonts[i]->DeleteObject();
1674 delete m_apFonts[i];
1675 m_apFonts[i] = NULL;
1678 if (m_pCacheBitmap != NULL)
1680 delete m_pCacheBitmap;
1681 m_pCacheBitmap = NULL;
1685 void CBaseView::OnSize(UINT nType, int cx, int cy)
1687 if (m_pCacheBitmap != NULL)
1689 m_pCacheBitmap->DeleteObject();
1690 delete m_pCacheBitmap;
1691 m_pCacheBitmap = NULL;
1693 // make sure the view header is redrawn
1694 CRect rcScroll;
1695 GetClientRect(&rcScroll);
1696 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1697 InvalidateRect(&rcScroll, FALSE);
1699 m_nScreenLines = -1;
1700 m_nScreenChars = -1;
1701 RecalcVertScrollBar();
1702 RecalcHorzScrollBar();
1703 CView::OnSize(nType, cx, cy);
1706 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
1708 if (m_pwndLeft)
1709 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
1710 if (m_pwndRight)
1711 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
1712 if (m_pwndBottom)
1713 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
1714 if (m_pwndLocator)
1715 m_pwndLocator->Invalidate();
1716 return CView::OnMouseWheel(nFlags, zDelta, pt);
1719 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
1721 if (GetKeyState(VK_CONTROL)&0x8000)
1723 // Ctrl-Wheel scrolls sideways
1724 ScrollSide(-zDelta/30);
1726 else
1728 int nLineCount = GetLineCount();
1729 int nTopLine = m_nTopLine;
1730 nTopLine -= (zDelta/30);
1731 if (nTopLine < 0)
1732 nTopLine = 0;
1733 if (nTopLine >= nLineCount)
1734 nTopLine = nLineCount - 1;
1735 ScrollToLine(nTopLine, TRUE);
1739 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1741 if (nHitTest == HTCLIENT)
1743 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
1744 return TRUE;
1746 return CView::OnSetCursor(pWnd, nHitTest, message);
1749 void CBaseView::OnKillFocus(CWnd* pNewWnd)
1751 CView::OnKillFocus(pNewWnd);
1752 m_bFocused = FALSE;
1753 UpdateCaret();
1754 Invalidate();
1757 void CBaseView::OnSetFocus(CWnd* pOldWnd)
1759 CView::OnSetFocus(pOldWnd);
1760 m_bFocused = TRUE;
1761 UpdateCaret();
1762 Invalidate();
1765 int CBaseView::GetLineFromPoint(CPoint point)
1767 ScreenToClient(&point);
1768 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
1771 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)
1773 return false;
1776 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
1778 int nLine = GetLineFromPoint(point);
1780 if (!m_pViewData)
1781 return;
1782 if (m_nSelBlockEnd >= GetLineCount())
1783 m_nSelBlockEnd = GetLineCount()-1;
1784 if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))
1786 int nIndex = nLine - 1;
1787 DiffStates state = m_pViewData->GetState(nIndex);
1788 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1790 // if there's nothing selected, or if the selection is outside the window then
1791 // select the diff block under the cursor.
1792 if (((m_nSelBlockStart<0)&&(m_nSelBlockEnd<0))||
1793 ((m_nSelBlockEnd < m_nTopLine)||(m_nSelBlockStart > m_nTopLine+m_nScreenLines)))
1795 while (nIndex >= 0)
1797 if (nIndex == 0)
1799 nIndex--;
1800 break;
1802 if (state != m_pViewData->GetState(--nIndex))
1803 break;
1805 m_nSelBlockStart = nIndex+1;
1806 while (nIndex < (m_pViewData->GetCount()-1))
1808 if (state != m_pViewData->GetState(++nIndex))
1809 break;
1811 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))
1812 m_nSelBlockEnd = nIndex;
1813 else
1814 m_nSelBlockEnd = nIndex-1;
1815 SetupSelection(m_nSelBlockStart, m_nSelBlockEnd);
1816 m_ptCaretPos.x = 0;
1817 m_ptCaretPos.y = nLine - 1;
1818 UpdateCaret();
1821 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&
1822 (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))
1824 // find a more 'relevant' state in the selection
1825 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)
1827 state = m_pViewData->GetState(i);
1828 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1829 break;
1832 bool bKeepSelection = OnContextMenu(point, nLine, state);
1833 if (! bKeepSelection)
1834 ClearSelection();
1835 RefreshViews();
1839 void CBaseView::RefreshViews()
1841 if (m_pwndLeft)
1843 m_pwndLeft->UpdateStatusBar();
1844 m_pwndLeft->Invalidate();
1846 if (m_pwndRight)
1848 m_pwndRight->UpdateStatusBar();
1849 m_pwndRight->Invalidate();
1851 if (m_pwndBottom)
1853 m_pwndBottom->UpdateStatusBar();
1854 m_pwndBottom->Invalidate();
1856 if (m_pwndLocator)
1857 m_pwndLocator->Invalidate();
1860 void CBaseView::GoToFirstDifference()
1862 m_ptCaretPos.y = 0;
1863 SelectNextBlock(1, false, false);
1866 void CBaseView::HiglightLines(int start, int end /* = -1 */)
1868 ClearSelection();
1869 m_nSelBlockStart = start;
1870 if (end < 0)
1871 end = start;
1872 m_nSelBlockEnd = end;
1873 m_ptCaretPos.x = 0;
1874 m_ptCaretPos.y = start;
1875 UpdateCaret();
1876 Invalidate();
1879 void CBaseView::SetupSelection(int start, int end)
1881 if (IsBottomViewGood())
1883 m_pwndBottom->m_nSelBlockStart = start;
1884 m_pwndBottom->m_nSelBlockEnd = end;
1885 m_pwndBottom->Invalidate();
1887 if (IsLeftViewGood())
1889 m_pwndLeft->m_nSelBlockStart = start;
1890 m_pwndLeft->m_nSelBlockEnd = end;
1891 m_pwndLeft->Invalidate();
1893 if (IsRightViewGood())
1895 m_pwndRight->m_nSelBlockStart = start;
1896 m_pwndRight->m_nSelBlockEnd = end;
1897 m_pwndRight->Invalidate();
1901 void CBaseView::OnMergePreviousconflict()
1903 SelectNextBlock(-1, true);
1906 void CBaseView::OnMergeNextconflict()
1908 SelectNextBlock(1, true);
1911 void CBaseView::OnMergeNextdifference()
1913 SelectNextBlock(1, false);
1916 void CBaseView::OnMergePreviousdifference()
1918 SelectNextBlock(-1, false);
1921 void CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */)
1923 if (! m_pViewData)
1924 return;
1926 if (m_pViewData->GetCount() == 0)
1927 return;
1929 int nCenterPos = m_ptCaretPos.y;
1930 int nLimit = 0;
1931 if (nDirection > 0)
1932 nLimit = m_pViewData->GetCount() - 1;
1934 if (nCenterPos >= m_pViewData->GetCount())
1935 nCenterPos = m_pViewData->GetCount()-1;
1937 if (bSkipEndOfCurrentBlock)
1939 // Find end of current block
1940 DiffStates state = m_pViewData->GetState(nCenterPos);
1941 while ((nCenterPos != nLimit) &&
1942 (m_pViewData->GetState(nCenterPos)==state))
1943 nCenterPos += nDirection;
1946 // Find next diff/conflict block
1947 while (nCenterPos != nLimit)
1949 DiffStates linestate = m_pViewData->GetState(nCenterPos);
1950 if (!bConflict &&
1951 (linestate != DIFFSTATE_NORMAL) &&
1952 (linestate != DIFFSTATE_UNKNOWN))
1953 break;
1954 if (bConflict &&
1955 ((linestate == DIFFSTATE_CONFLICTADDED) ||
1956 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
1957 (linestate == DIFFSTATE_CONFLICTED) ||
1958 (linestate == DIFFSTATE_CONFLICTEMPTY)))
1959 break;
1961 nCenterPos += nDirection;
1964 // Find end of new block
1965 DiffStates state = m_pViewData->GetState(nCenterPos);
1966 int nBlockEnd = nCenterPos;
1967 while ((nBlockEnd != nLimit) &&
1968 (state == m_pViewData->GetState(nBlockEnd + nDirection)))
1969 nBlockEnd += nDirection;
1971 int nTopPos = nCenterPos - (GetScreenLines()/2);
1972 if (nTopPos < 0)
1973 nTopPos = 0;
1975 m_ptCaretPos.x = 0;
1976 m_ptCaretPos.y = nCenterPos;
1977 ClearSelection();
1978 if (nDirection > 0)
1979 SetupSelection(nCenterPos, nBlockEnd);
1980 else
1981 SetupSelection(nBlockEnd, nCenterPos);
1983 ScrollAllToLine(nTopPos, FALSE);
1984 RecalcAllVertScrollBars(TRUE);
1985 m_nCaretGoalPos = 0;
1986 UpdateCaret();
1987 ShowDiffLines(nCenterPos);
1990 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
1992 // need to handle both ANSI and UNICODE versions of the message
1993 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
1994 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
1995 CString strTipText;
1996 UINT nID = (UINT)pNMHDR->idFrom;
1997 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
1998 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2000 // idFrom is actually the HWND of the tool
2001 nID = ::GetDlgCtrlID((HWND)nID);
2004 if (pNMHDR->idFrom == (UINT)m_hWnd)
2006 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
2008 strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;
2010 else
2012 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2015 else
2016 return FALSE;
2018 *pResult = 0;
2019 if (strTipText.IsEmpty())
2020 return TRUE;
2022 if (pNMHDR->code == TTN_NEEDTEXTA)
2024 pTTTA->lpszText = m_szTip;
2025 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2027 else
2029 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2030 pTTTW->lpszText = m_wszTip;
2033 return TRUE; // message was handled
2037 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2039 CRect rcClient;
2040 GetClientRect(rcClient);
2041 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2042 if (textrect.PtInRect(point))
2044 // inside the header part of the view (showing the filename)
2045 pTI->hwnd = this->m_hWnd;
2046 this->GetClientRect(&pTI->rect);
2047 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2048 pTI->uId = (UINT)m_hWnd;
2049 pTI->lpszText = LPSTR_TEXTCALLBACK;
2051 // we want multi line tooltips
2052 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2053 if (pToolTip->GetSafeHwnd() != NULL)
2055 pToolTip->SetMaxTipWidth(INT_MAX);
2058 return 1;
2060 return -1;
2063 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2065 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2066 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2067 switch (nChar)
2069 case VK_PRIOR:
2071 m_ptCaretPos.y -= GetScreenLines();
2072 m_ptCaretPos.y = max(m_ptCaretPos.y, 0);
2073 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2074 if (bShift)
2075 AdjustSelection();
2076 else
2077 ClearSelection();
2078 UpdateCaret();
2079 EnsureCaretVisible();
2080 ShowDiffLines(m_ptCaretPos.y);
2082 break;
2083 case VK_NEXT:
2085 m_ptCaretPos.y += GetScreenLines();
2086 if (m_ptCaretPos.y >= GetLineCount())
2087 m_ptCaretPos.y = GetLineCount()-1;
2088 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2089 if (bShift)
2090 AdjustSelection();
2091 else
2092 ClearSelection();
2093 UpdateCaret();
2094 EnsureCaretVisible();
2095 ShowDiffLines(m_ptCaretPos.y);
2097 break;
2098 case VK_HOME:
2100 if (bControl)
2102 ScrollAllToLine(0);
2103 m_ptCaretPos.x = 0;
2104 m_ptCaretPos.y = 0;
2105 m_nCaretGoalPos = 0;
2106 if (bShift)
2107 AdjustSelection();
2108 else
2109 ClearSelection();
2110 UpdateCaret();
2112 else
2114 m_ptCaretPos.x = 0;
2115 m_nCaretGoalPos = 0;
2116 if (bShift)
2117 AdjustSelection();
2118 else
2119 ClearSelection();
2120 EnsureCaretVisible();
2121 UpdateCaret();
2124 break;
2125 case VK_END:
2127 if (bControl)
2129 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2130 m_ptCaretPos.y = GetLineCount()-1;
2131 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2132 UpdateGoalPos();
2133 if (bShift)
2134 AdjustSelection();
2135 else
2136 ClearSelection();
2137 UpdateCaret();
2139 else
2141 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2142 UpdateGoalPos();
2143 if (bShift)
2144 AdjustSelection();
2145 else
2146 ClearSelection();
2147 EnsureCaretVisible();
2148 UpdateCaret();
2151 break;
2152 case VK_BACK:
2154 if (m_bCaretHidden)
2155 break;
2157 if (! HasTextSelection()) {
2158 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)
2159 break;
2160 m_ptSelectionEndPos = m_ptCaretPos;
2161 MoveCaretLeft();
2162 m_ptSelectionStartPos = m_ptCaretPos;
2164 RemoveSelectedText();
2166 break;
2167 case VK_DELETE:
2169 if (m_bCaretHidden)
2170 break;
2172 if (! HasTextSelection()) {
2173 if (! MoveCaretRight())
2174 break;
2175 m_ptSelectionEndPos = m_ptCaretPos;
2176 MoveCaretLeft();
2177 m_ptSelectionStartPos = m_ptCaretPos;
2179 RemoveSelectedText();
2181 break;
2183 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2186 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2188 int nClickedLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2189 nClickedLine--; //we need the index
2190 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2192 m_ptCaretPos.y = nClickedLine;
2193 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2194 UpdateGoalPos();
2196 if (nFlags & MK_SHIFT)
2197 AdjustSelection();
2198 else
2200 ClearSelection();
2201 SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);
2204 UpdateCaret();
2206 Invalidate();
2209 CView::OnLButtonDown(nFlags, point);
2212 void CBaseView::OnEditCopy()
2214 if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))
2215 return;
2216 // first store the selected lines in one CString
2217 CString sCopyData;
2218 for (int i=m_ptSelectionStartPos.y; i<=m_ptSelectionEndPos.y; i++)
2220 switch (m_pViewData->GetState(i))
2222 case DIFFSTATE_EMPTY:
2223 break;
2224 case DIFFSTATE_UNKNOWN:
2225 case DIFFSTATE_NORMAL:
2226 case DIFFSTATE_REMOVED:
2227 case DIFFSTATE_REMOVEDWHITESPACE:
2228 case DIFFSTATE_ADDED:
2229 case DIFFSTATE_ADDEDWHITESPACE:
2230 case DIFFSTATE_WHITESPACE:
2231 case DIFFSTATE_WHITESPACE_DIFF:
2232 case DIFFSTATE_CONFLICTED:
2233 case DIFFSTATE_CONFLICTED_IGNORED:
2234 case DIFFSTATE_CONFLICTADDED:
2235 case DIFFSTATE_CONFLICTEMPTY:
2236 case DIFFSTATE_CONFLICTRESOLVED:
2237 case DIFFSTATE_IDENTICALREMOVED:
2238 case DIFFSTATE_IDENTICALADDED:
2239 case DIFFSTATE_THEIRSREMOVED:
2240 case DIFFSTATE_THEIRSADDED:
2241 case DIFFSTATE_YOURSREMOVED:
2242 case DIFFSTATE_YOURSADDED:
2243 case DIFFSTATE_EDITED:
2244 sCopyData += m_pViewData->GetLine(i);
2245 sCopyData += _T("\r\n");
2246 break;
2249 // remove the last \r\n
2250 sCopyData = sCopyData.Left(sCopyData.GetLength()-2);
2251 // remove the non-selected chars from the first line
2252 sCopyData = sCopyData.Mid(m_ptSelectionStartPos.x);
2253 // remove the non-selected chars from the last line
2254 int lastLinePos = sCopyData.ReverseFind('\n');
2255 lastLinePos += 1;
2256 if (lastLinePos == 0)
2257 lastLinePos -= m_ptSelectionStartPos.x;
2258 sCopyData = sCopyData.Left(lastLinePos+m_ptSelectionEndPos.x);
2259 if (!sCopyData.IsEmpty())
2261 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
2265 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
2267 if (m_pMainFrame->m_nMoveMovesToIgnore > 0) {
2268 --m_pMainFrame->m_nMoveMovesToIgnore;
2269 CView::OnMouseMove(nFlags, point);
2270 return;
2273 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2274 nMouseLine--; //we need the index
2275 if (nMouseLine < -1)
2277 nMouseLine = -1;
2279 ShowDiffLines(nMouseLine);
2281 KillTimer(IDT_SCROLLTIMER);
2282 if (nFlags & MK_LBUTTON)
2284 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2285 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2286 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2287 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&
2288 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
2290 m_ptCaretPos.y = nMouseLine;
2291 m_ptCaretPos.x = charIndex;
2292 UpdateGoalPos();
2293 AdjustSelection();
2294 UpdateCaret();
2295 Invalidate();
2296 UpdateWindow();
2298 if (nMouseLine < m_nTopLine)
2300 ScrollToLine(m_nTopLine-1, TRUE);
2301 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2303 if (nMouseLine >= m_nTopLine + GetScreenLines())
2305 ScrollToLine(m_nTopLine+1, TRUE);
2306 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2308 if (charIndex <= m_nOffsetChar)
2310 ScrollSide(-1);
2311 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2313 if (charIndex >= (GetScreenChars()+m_nOffsetChar))
2315 ScrollSide(1);
2316 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2320 if (!m_bMouseWithin)
2322 m_bMouseWithin = TRUE;
2323 TRACKMOUSEEVENT tme;
2324 tme.cbSize = sizeof(TRACKMOUSEEVENT);
2325 tme.dwFlags = TME_LEAVE;
2326 tme.hwndTrack = m_hWnd;
2327 _TrackMouseEvent(&tme);
2330 CView::OnMouseMove(nFlags, point);
2333 void CBaseView::OnMouseLeave()
2335 ShowDiffLines(-1);
2336 m_bMouseWithin = FALSE;
2337 KillTimer(IDT_SCROLLTIMER);
2338 CView::OnMouseLeave();
2341 void CBaseView::OnTimer(UINT_PTR nIDEvent)
2343 if (nIDEvent == IDT_SCROLLTIMER)
2345 POINT point;
2346 GetCursorPos(&point);
2347 ScreenToClient(&point);
2348 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2349 nMouseLine--; //we need the index
2350 if (nMouseLine < -1)
2352 nMouseLine = -1;
2354 if (GetKeyState(VK_LBUTTON)&0x8000)
2356 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2357 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2358 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2359 if (nMouseLine < m_nTopLine)
2361 ScrollToLine(m_nTopLine-1, TRUE);
2362 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2364 if (nMouseLine >= m_nTopLine + GetScreenLines())
2366 ScrollToLine(m_nTopLine+1, TRUE);
2367 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2369 if (charIndex <= m_nOffsetChar)
2371 ScrollSide(-1);
2372 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2374 if (charIndex >= GetScreenChars())
2376 ScrollSide(1);
2377 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2383 CView::OnTimer(nIDEvent);
2386 void CBaseView::SelectLines(int nLine1, int nLine2)
2388 if (nLine2 == -1)
2389 nLine2 = nLine1;
2390 m_nSelBlockStart = nLine1;
2391 m_nSelBlockEnd = nLine2;
2392 Invalidate();
2395 void CBaseView::ShowDiffLines(int nLine)
2397 if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))
2399 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))
2401 nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);
2402 nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);
2404 if (nLine >= 0)
2406 if (nLine != m_nMouseLine)
2408 m_nMouseLine = nLine;
2409 if (nLine >= GetLineCount())
2410 nLine = -1;
2411 m_pwndLineDiffBar->ShowLines(nLine);
2416 else
2418 m_pwndLineDiffBar->ShowLines(nLine);
2422 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2424 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2425 return;
2426 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2428 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2429 m_pwndBottom->m_pViewData->SetLine(i, m_pwndLeft->m_pViewData->GetLine(i));
2430 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2431 m_pwndBottom->m_pViewData->SetState(i, m_pwndLeft->m_pViewData->GetState(i));
2432 m_pwndBottom->m_pViewData->SetLineEnding(i, m_pwndBottom->lineendings);
2433 if (m_pwndBottom->IsLineConflicted(i))
2435 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2436 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2437 else
2438 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2440 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2443 // your block is done, now insert their block
2444 int index = m_nSelBlockEnd+1;
2445 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2447 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2448 m_pwndBottom->m_pViewData->InsertData(index, m_pwndRight->m_pViewData->GetData(i));
2449 if (m_pwndBottom->IsLineConflicted(index))
2451 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2452 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2453 else
2454 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2456 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2457 index++;
2459 // adjust line numbers
2460 for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)
2462 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2463 if (oldline >= 0)
2464 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2467 // now insert an empty block in both yours and theirs
2468 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2470 rightstate.addedlines.push_back(m_nSelBlockStart);
2471 m_pwndRight->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2472 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2473 leftstate.addedlines.push_back(m_nSelBlockEnd+1);
2475 RecalcAllVertScrollBars();
2476 m_pwndBottom->SetModified();
2477 m_pwndLeft->SetModified();
2478 m_pwndRight->SetModified();
2481 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2483 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2484 return;
2485 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2487 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2488 m_pwndBottom->m_pViewData->SetLine(i, m_pwndRight->m_pViewData->GetLine(i));
2489 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2490 m_pwndBottom->m_pViewData->SetState(i, m_pwndRight->m_pViewData->GetState(i));
2491 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2492 m_pwndBottom->m_pViewData->SetLineEnding(i, m_pwndBottom->lineendings);
2493 if (m_pwndBottom->IsLineConflicted(i))
2495 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2496 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2497 else
2498 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2500 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2503 // your block is done, now insert their block
2504 int index = m_nSelBlockEnd+1;
2505 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2507 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2508 m_pwndBottom->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2509 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2510 if (m_pwndBottom->IsLineConflicted(index))
2512 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2513 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2514 else
2515 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2517 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2518 index++;
2520 // adjust line numbers
2521 for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)
2523 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2524 if (oldline >= 0)
2525 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2528 // now insert an empty block in both yours and theirs
2529 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2531 leftstate.addedlines.push_back(m_nSelBlockStart);
2532 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2533 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2534 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2537 RecalcAllVertScrollBars();
2538 m_pwndBottom->SetModified();
2539 m_pwndLeft->SetModified();
2540 m_pwndRight->SetModified();
2543 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)
2545 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2546 return;
2547 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2549 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2550 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2553 // your block is done, now insert their block
2554 int index = m_nSelBlockEnd+1;
2555 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2557 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2558 m_pwndRight->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2559 m_pwndRight->m_pViewData->SetState(index++, DIFFSTATE_THEIRSADDED);
2561 // adjust line numbers
2562 index--;
2563 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2565 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2566 if (oldline >= 0)
2567 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2570 // now insert an empty block in the left view
2571 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2573 leftstate.addedlines.push_back(m_nSelBlockStart);
2574 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2576 RecalcAllVertScrollBars();
2577 m_pwndLeft->SetModified();
2578 m_pwndRight->SetModified();
2581 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)
2583 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2584 return;
2585 // get line number from just before the block
2586 long linenumber = 0;
2587 if (m_nSelBlockStart > 0)
2588 linenumber = m_pwndRight->m_pViewData->GetLineNumber(m_nSelBlockStart-1);
2589 linenumber++;
2590 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2592 rightstate.addedlines.push_back(m_nSelBlockStart);
2593 m_pwndRight->m_pViewData->InsertData(i, m_pwndLeft->m_pViewData->GetLine(i), DIFFSTATE_THEIRSADDED, linenumber++, m_pwndLeft->m_pViewData->GetLineEnding(i));
2595 // adjust line numbers
2596 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2598 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2599 if (oldline >= 0)
2600 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);
2603 // now insert an empty block in left view
2604 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2606 leftstate.addedlines.push_back(m_nSelBlockEnd + 1);
2607 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd + 1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2609 RecalcAllVertScrollBars();
2610 m_pwndLeft->SetModified();
2611 m_pwndRight->SetModified();
2614 void CBaseView::UpdateCaret()
2616 if (m_ptCaretPos.y >= GetLineCount())
2617 m_ptCaretPos.y = GetLineCount()-1;
2618 if (m_ptCaretPos.y < 0)
2619 m_ptCaretPos.y = 0;
2620 if (m_ptCaretPos.x > GetLineLength(m_ptCaretPos.y))
2621 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2622 if (m_ptCaretPos.x < 0)
2623 m_ptCaretPos.x = 0;
2625 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2627 if (m_bFocused && !m_bCaretHidden &&
2628 m_ptCaretPos.y >= m_nTopLine &&
2629 m_ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
2630 nCaretOffset >= m_nOffsetChar &&
2631 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
2633 CreateSolidCaret(2, GetLineHeight());
2634 SetCaretPos(TextToClient(m_ptCaretPos));
2635 ShowCaret();
2637 else
2639 HideCaret();
2643 void CBaseView::EnsureCaretVisible()
2645 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2647 if (m_ptCaretPos.y < m_nTopLine)
2648 ScrollAllToLine(m_ptCaretPos.y);
2649 if (m_ptCaretPos.y >= (m_nTopLine+GetScreenLines()))
2650 ScrollAllToLine(m_ptCaretPos.y-GetScreenLines()+1);
2651 if (nCaretOffset < m_nOffsetChar)
2652 ScrollToChar(nCaretOffset);
2653 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
2654 ScrollToChar(nCaretOffset-GetScreenChars()+1);
2657 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const
2659 int nLength = GetLineLength(nLineIndex);
2660 ASSERT(nCharIndex >= 0);
2661 if (nCharIndex > nLength)
2662 nCharIndex = nLength;
2663 LPCTSTR pszChars = GetLineChars(nLineIndex);
2664 int nOffset = 0;
2665 int nTabSize = GetTabSize();
2666 for (int I = 0; I < nCharIndex; I ++)
2668 if (pszChars[I] == _T('\t'))
2669 nOffset += (nTabSize - nOffset % nTabSize);
2670 else
2671 nOffset++;
2673 return nOffset;
2676 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const
2678 int nLength = GetLineLength(nLineIndex);
2679 LPCTSTR pszLine = GetLineChars(nLineIndex);
2680 int nIndex = 0;
2681 int nOffset = 0;
2682 int nTabSize = GetTabSize();
2683 while (nOffset < nActualOffset && nIndex < nLength)
2685 if (pszLine[nIndex] == _T('\t'))
2686 nOffset += (nTabSize - nOffset % nTabSize);
2687 else
2688 ++nOffset;
2689 ++nIndex;
2691 return nIndex;
2694 POINT CBaseView::TextToClient(const POINT& point)
2696 POINT pt;
2697 pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());
2698 pt.x = CalculateActualOffset(point.y, point.x);
2700 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();
2701 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
2702 return pt;
2705 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
2707 CView::OnChar(nChar, nRepCnt, nFlags);
2709 if (m_bCaretHidden)
2710 return;
2712 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
2713 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
2714 return;
2716 if ((nChar > 31)||(nChar == VK_TAB))
2718 RemoveSelectedText();
2719 AddUndoLine(m_ptCaretPos.y);
2720 CString sLine = GetLineChars(m_ptCaretPos.y);
2721 sLine.Insert(m_ptCaretPos.x, (wchar_t)nChar);
2722 m_pViewData->SetLine(m_ptCaretPos.y, sLine);
2723 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);
2724 if (m_pViewData->GetLineEnding(m_ptCaretPos.y) == EOL_NOENDING && m_pViewData->GetCount() - 1 != m_ptCaretPos.y)
2725 m_pViewData->SetLineEnding(m_ptCaretPos.y, lineendings);
2726 m_ptCaretPos.x++;
2727 UpdateGoalPos();
2729 else if (nChar == 10)
2731 AddUndoLine(m_ptCaretPos.y);
2732 EOL eol = m_pViewData->GetLineEnding(m_ptCaretPos.y);
2733 EOL newEOL = EOL_CRLF;
2734 switch (eol) {
2735 case EOL_CRLF:
2736 newEOL = EOL_CR;
2737 break;
2738 case EOL_CR:
2739 newEOL = EOL_LF;
2740 break;
2741 case EOL_LF:
2742 newEOL = (m_pViewData->GetCount() - 1 == m_ptCaretPos.y && !m_pViewData->GetLine(m_ptCaretPos.y).IsEmpty()) ? EOL_NOENDING : EOL_CRLF;
2743 break;
2745 m_pViewData->SetLineEnding(m_ptCaretPos.y, newEOL);
2746 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);
2747 UpdateGoalPos();
2749 else if (nChar == VK_RETURN)
2751 // insert a new, fresh and empty line below the cursor
2752 RemoveSelectedText();
2753 AddUndoLine(m_ptCaretPos.y, true);
2754 EOL eOriginalEnding = m_pViewData->GetLineEnding(m_ptCaretPos.y);
2755 if (m_pViewData->GetCount() - 2 == m_ptCaretPos.y && eOriginalEnding == EOL_NOENDING)
2756 m_pViewData->SetLineEnding(m_ptCaretPos.y, lineendings);
2757 // move the cursor to the new line
2758 m_ptCaretPos.y++;
2759 m_ptCaretPos.x = 0;
2760 if (m_pViewData->GetCount() - 1 == m_ptCaretPos.y)
2761 m_pViewData->SetLineEnding(m_ptCaretPos.y, eOriginalEnding);
2762 UpdateGoalPos();
2764 else
2765 return; // Unknown control character -- ignore it.
2766 ClearSelection();
2767 EnsureCaretVisible();
2768 UpdateCaret();
2769 SetModified(true);
2770 Invalidate(FALSE);
2773 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)
2775 viewstate leftstate;
2776 viewstate rightstate;
2777 viewstate bottomstate;
2778 leftstate.AddLineFormView(m_pwndLeft, nLine, bAddEmptyLine);
2779 rightstate.AddLineFormView(m_pwndRight, nLine, bAddEmptyLine);
2780 bottomstate.AddLineFormView(m_pwndBottom, nLine, bAddEmptyLine);
2781 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2784 void CBaseView::AddEmptyLine(int nLineIndex)
2786 if (m_pViewData == NULL)
2787 return;
2788 if (!m_bCaretHidden)
2790 CString sPartLine = GetLineChars(nLineIndex);
2791 m_pViewData->SetLine(nLineIndex, sPartLine.Left(m_ptCaretPos.x));
2792 sPartLine = sPartLine.Mid(m_ptCaretPos.x);
2793 m_pViewData->InsertData(nLineIndex+1, sPartLine, DIFFSTATE_EDITED, -1, lineendings);
2795 else
2796 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, lineendings);
2797 Invalidate(FALSE);
2800 void CBaseView::RemoveLine(int nLineIndex)
2802 if (m_pViewData == NULL)
2803 return;
2804 m_pViewData->RemoveData(nLineIndex);
2805 if (m_ptCaretPos.y >= GetLineCount())
2806 m_ptCaretPos.y = GetLineCount()-1;
2807 Invalidate(FALSE);
2810 void CBaseView::RemoveSelectedText()
2812 if (m_pViewData == NULL)
2813 return;
2814 if (!HasTextSelection())
2815 return;
2817 viewstate rightstate;
2818 viewstate bottomstate;
2819 viewstate leftstate;
2820 std::vector<LONG> linestoremove;
2821 for (LONG i = m_ptSelectionStartPos.y; i <= m_ptSelectionEndPos.y; ++i)
2823 if (i == m_ptSelectionStartPos.y)
2825 CString sLine = GetLineChars(m_ptSelectionStartPos.y);
2826 CString newLine;
2827 if (i == m_ptSelectionStartPos.y)
2829 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2831 leftstate.difflines[i] = m_pwndLeft->m_pViewData->GetLine(i);
2832 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2834 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2836 rightstate.difflines[i] = m_pwndRight->m_pViewData->GetLine(i);
2837 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2839 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2841 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2842 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2844 newLine = sLine.Left(m_ptSelectionStartPos.x);
2845 sLine = GetLineChars(m_ptSelectionEndPos.y);
2846 newLine = newLine + sLine.Mid(m_ptSelectionEndPos.x);
2848 m_pViewData->SetLine(i, newLine);
2849 m_pViewData->SetState(i, DIFFSTATE_EDITED);
2850 SetModified();
2852 else
2854 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2856 leftstate.removedlines[i] = m_pwndLeft->m_pViewData->GetData(i);
2858 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2860 rightstate.removedlines[i] = m_pwndRight->m_pViewData->GetData(i);
2862 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2864 bottomstate.removedlines[i] = m_pwndBottom->m_pViewData->GetData(i);
2866 linestoremove.push_back(i);
2869 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2870 // remove the lines at the end, to avoid problems with line indexes
2871 if (linestoremove.size())
2873 std::vector<LONG>::const_iterator it = linestoremove.begin();
2874 int nLineToRemove = *it;
2875 for ( ; it != linestoremove.end(); ++it)
2877 if (m_pwndLeft)
2878 m_pwndLeft->RemoveLine(nLineToRemove);
2879 if (m_pwndRight)
2880 m_pwndRight->RemoveLine(nLineToRemove);
2881 if (m_pwndBottom)
2882 m_pwndBottom->RemoveLine(nLineToRemove);
2883 SetModified();
2886 m_ptCaretPos = m_ptSelectionStartPos;
2887 UpdateGoalPos();
2888 ClearSelection();
2889 UpdateCaret();
2890 EnsureCaretVisible();
2891 Invalidate(FALSE);
2894 void CBaseView::PasteText()
2896 if (!OpenClipboard())
2897 return;
2899 CString sClipboardText;
2900 HGLOBAL hglb = GetClipboardData(CF_TEXT);
2901 if (hglb)
2903 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
2904 sClipboardText = CString(lpstr);
2905 GlobalUnlock(hglb);
2907 hglb = GetClipboardData(CF_UNICODETEXT);
2908 if (hglb)
2910 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
2911 sClipboardText = lpstr;
2912 GlobalUnlock(hglb);
2914 CloseClipboard();
2916 if (sClipboardText.IsEmpty())
2917 return;
2919 sClipboardText.Replace(_T("\r\n"), _T("\r"));
2920 sClipboardText.Replace('\n', '\r');
2921 // We want to undo the insertion in a single step.
2922 CUndo::GetInstance().BeginGrouping();
2923 // use the easy way to insert text:
2924 // insert char by char, using the OnChar() method
2925 for (int i=0; i<sClipboardText.GetLength(); ++i)
2927 OnChar(sClipboardText[i], 0, 0);
2929 CUndo::GetInstance().EndGrouping();
2932 void CBaseView::OnCaretDown()
2934 m_ptCaretPos.y++;
2935 m_ptCaretPos.y = min(m_ptCaretPos.y, GetLineCount()-1);
2936 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2937 if (GetKeyState(VK_SHIFT)&0x8000)
2938 AdjustSelection();
2939 else
2940 ClearSelection();
2941 UpdateCaret();
2942 EnsureCaretVisible();
2943 ShowDiffLines(m_ptCaretPos.y);
2946 bool CBaseView::MoveCaretLeft()
2948 if (m_ptCaretPos.x == 0)
2950 if (m_ptCaretPos.y > 0)
2952 --m_ptCaretPos.y;
2953 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2955 else
2956 return false;
2958 else
2959 --m_ptCaretPos.x;
2961 UpdateGoalPos();
2962 return true;
2965 bool CBaseView::MoveCaretRight()
2967 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
2969 if (m_ptCaretPos.y < (GetLineCount() - 1))
2971 ++m_ptCaretPos.y;
2972 m_ptCaretPos.x = 0;
2974 else
2975 return false;
2977 else
2978 ++m_ptCaretPos.x;
2980 UpdateGoalPos();
2981 return true;
2984 void CBaseView::UpdateGoalPos()
2986 m_nCaretGoalPos = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2989 void CBaseView::OnCaretLeft()
2991 MoveCaretLeft();
2992 if (GetKeyState(VK_SHIFT)&0x8000)
2993 AdjustSelection();
2994 else
2995 ClearSelection();
2996 EnsureCaretVisible();
2997 UpdateCaret();
3000 void CBaseView::OnCaretRight()
3002 MoveCaretRight();
3003 if (GetKeyState(VK_SHIFT)&0x8000)
3004 AdjustSelection();
3005 else
3006 ClearSelection();
3007 EnsureCaretVisible();
3008 UpdateCaret();
3011 void CBaseView::OnCaretUp()
3013 m_ptCaretPos.y--;
3014 m_ptCaretPos.y = max(0, m_ptCaretPos.y);
3015 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
3016 if (GetKeyState(VK_SHIFT)&0x8000)
3017 AdjustSelection();
3018 else
3019 ClearSelection();
3020 UpdateCaret();
3021 EnsureCaretVisible();
3022 ShowDiffLines(m_ptCaretPos.y);
3025 bool CBaseView::IsWordSeparator(wchar_t ch) const
3027 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3030 bool CBaseView::IsCaretAtWordBoundary() const
3032 LPCTSTR line = GetLineChars(m_ptCaretPos.y);
3033 if (!*line)
3034 return false; // no boundary at the empty lines
3035 if (m_ptCaretPos.x == 0)
3036 return !IsWordSeparator(line[m_ptCaretPos.x]);
3037 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
3038 return !IsWordSeparator(line[m_ptCaretPos.x - 1]);
3039 return
3040 IsWordSeparator(line[m_ptCaretPos.x]) !=
3041 IsWordSeparator(line[m_ptCaretPos.x - 1]);
3044 void CBaseView::OnCaretWordleft()
3046 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3049 if (GetKeyState(VK_SHIFT)&0x8000)
3050 AdjustSelection();
3051 else
3052 ClearSelection();
3053 EnsureCaretVisible();
3054 UpdateCaret();
3057 void CBaseView::OnCaretWordright()
3059 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3062 if (GetKeyState(VK_SHIFT)&0x8000)
3063 AdjustSelection();
3064 else
3065 ClearSelection();
3066 EnsureCaretVisible();
3067 UpdateCaret();
3070 void CBaseView::ClearCurrentSelection()
3072 m_ptSelectionStartPos = m_ptCaretPos;
3073 m_ptSelectionEndPos = m_ptCaretPos;
3074 m_ptSelectionOrigin = m_ptCaretPos;
3075 m_nSelBlockStart = -1;
3076 m_nSelBlockEnd = -1;
3077 Invalidate(FALSE);
3080 void CBaseView::ClearSelection()
3082 if (m_pwndLeft)
3083 m_pwndLeft->ClearCurrentSelection();
3084 if (m_pwndRight)
3085 m_pwndRight->ClearCurrentSelection();
3086 if (m_pwndBottom)
3087 m_pwndBottom->ClearCurrentSelection();
3090 void CBaseView::AdjustSelection()
3092 if ((m_ptCaretPos.y < m_ptSelectionOrigin.y) ||
3093 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x <= m_ptSelectionOrigin.x))
3095 m_ptSelectionStartPos = m_ptCaretPos;
3096 m_ptSelectionEndPos = m_ptSelectionOrigin;
3099 if ((m_ptCaretPos.y > m_ptSelectionOrigin.y) ||
3100 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x >= m_ptSelectionOrigin.x))
3102 m_ptSelectionStartPos = m_ptSelectionOrigin;
3103 m_ptSelectionEndPos = m_ptCaretPos;
3106 SetupSelection(min(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y), max(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y));
3108 Invalidate(FALSE);
3111 void CBaseView::OnEditCut()
3113 if (!m_bCaretHidden)
3115 OnEditCopy();
3116 RemoveSelectedText();
3120 void CBaseView::OnEditPaste()
3122 if (!m_bCaretHidden)
3124 PasteText();
3128 void CBaseView::OnEditSelectall()
3130 int nCount = GetLineCount();
3131 SetupSelection(0, nCount);
3132 m_ptSelectionStartPos.x = 0;
3133 m_ptSelectionStartPos.y = 0;
3135 m_ptSelectionEndPos.y = nCount-1;
3136 CString sLine = GetLineChars(nCount-1);
3137 m_ptSelectionEndPos.x = sLine.GetLength();
3139 UpdateWindow();