Fixed issue #1507: Submodule Diff Dialog should show dirty state only on working...
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob2397a255698592c127f0e4300a00e4937e17b922
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::UNICODE_BE:
286 sBarText = _T("UTF-16BE ");
287 break;
288 case CFileTextLines::UTF8:
289 sBarText = _T("UTF8 ");
290 break;
291 case CFileTextLines::UTF8BOM:
292 sBarText = _T("UTF8 BOM ");
293 break;
296 switch(lineendings)
298 case EOL_LF:
299 sBarText += _T("LF ");
300 break;
301 case EOL_CRLF:
302 sBarText += _T("CRLF ");
303 break;
304 case EOL_LFCR:
305 sBarText += _T("LFCR ");
306 break;
307 case EOL_CR:
308 sBarText += _T("CR ");
309 break;
312 if (sBarText.IsEmpty())
313 sBarText += _T(" / ");
315 if (nRemovedLines)
317 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
318 if (!sBarText.IsEmpty())
319 sBarText += _T(" / ");
320 sBarText += sTemp;
322 if (nAddedLines)
324 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
325 if (!sBarText.IsEmpty())
326 sBarText += _T(" / ");
327 sBarText += sTemp;
329 if (nConflictedLines)
331 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
332 if (!sBarText.IsEmpty())
333 sBarText += _T(" / ");
334 sBarText += sTemp;
336 if (m_pwndStatusBar)
338 UINT nID;
339 UINT nStyle;
340 int cxWidth;
341 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
342 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
344 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
346 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
348 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
349 sBarText = sTemp+sBarText;
351 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
353 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
354 sBarText = sTemp+sBarText;
356 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
357 //calculate the width of the text
358 CDC * pDC = m_pwndStatusBar->GetDC();
359 if (pDC)
361 CSize size = pDC->GetTextExtent(sBarText);
362 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
363 ReleaseDC(pDC);
365 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
369 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
371 if (!CView::PreCreateWindow(cs))
372 return FALSE;
374 cs.dwExStyle |= WS_EX_CLIENTEDGE;
375 cs.style &= ~WS_BORDER;
376 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
377 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
379 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
380 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
382 // View must always create its own scrollbars,
383 // if only it's not used within splitter
384 cs.style |= (WS_HSCROLL | WS_VSCROLL);
386 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
387 return TRUE;
390 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/, BOOL bStrikeOut /*= FALSE*/)
392 int nIndex = 0;
393 if (bBold)
394 nIndex |= 1;
395 if (bItalic)
396 nIndex |= 2;
397 if (bStrikeOut)
398 nIndex |= 4;
399 if (m_apFonts[nIndex] == NULL)
401 m_apFonts[nIndex] = new CFont;
402 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
403 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
404 m_lfBaseFont.lfItalic = (BYTE) bItalic;
405 m_lfBaseFont.lfStrikeOut = (BYTE) bStrikeOut;
406 if (bStrikeOut)
407 m_lfBaseFont.lfStrikeOut = (BYTE)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE);
408 CDC * pDC = GetDC();
409 if (pDC)
411 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
412 ReleaseDC(pDC);
414 _tcsncpy_s(m_lfBaseFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
415 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
417 delete m_apFonts[nIndex];
418 m_apFonts[nIndex] = NULL;
419 return CView::GetFont();
422 return m_apFonts[nIndex];
425 void CBaseView::CalcLineCharDim()
427 CDC *pDC = GetDC();
428 CFont *pOldFont = pDC->SelectObject(GetFont());
429 CSize szCharExt = pDC->GetTextExtent(_T("X"));
430 m_nLineHeight = szCharExt.cy;
431 if (m_nLineHeight <= 0)
432 m_nLineHeight = -1;
433 m_nCharWidth = szCharExt.cx;
434 if (m_nCharWidth <= 0)
435 m_nCharWidth = -1;
436 pDC->SelectObject(pOldFont);
437 ReleaseDC(pDC);
440 int CBaseView::GetScreenChars()
442 if (m_nScreenChars == -1)
444 CRect rect;
445 GetClientRect(&rect);
446 m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
448 return m_nScreenChars;
451 int CBaseView::GetAllMinScreenChars() const
453 int nChars = 0;
454 if (IsLeftViewGood())
455 nChars = m_pwndLeft->GetScreenChars();
456 if (IsRightViewGood())
457 nChars = (nChars < m_pwndRight->GetScreenChars() ? nChars : m_pwndRight->GetScreenChars());
458 if (IsBottomViewGood())
459 nChars = (nChars < m_pwndBottom->GetScreenChars() ? nChars : m_pwndBottom->GetScreenChars());
460 return nChars;
463 int CBaseView::GetAllMaxLineLength() const
465 int nLength = 0;
466 if (IsLeftViewGood())
467 nLength = m_pwndLeft->GetMaxLineLength();
468 if (IsRightViewGood())
469 nLength = (nLength > m_pwndRight->GetMaxLineLength() ? nLength : m_pwndRight->GetMaxLineLength());
470 if (IsBottomViewGood())
471 nLength = (nLength > m_pwndBottom->GetMaxLineLength() ? nLength : m_pwndBottom->GetMaxLineLength());
472 return nLength;
475 int CBaseView::GetLineHeight()
477 if (m_nLineHeight == -1)
478 CalcLineCharDim();
479 if (m_nLineHeight <= 0)
480 return 1;
481 return m_nLineHeight;
484 int CBaseView::GetCharWidth()
486 if (m_nCharWidth == -1)
487 CalcLineCharDim();
488 if (m_nCharWidth <= 0)
489 return 1;
490 return m_nCharWidth;
493 int CBaseView::GetMaxLineLength()
495 if (m_nMaxLineLength == -1)
497 m_nMaxLineLength = 0;
498 int nLineCount = GetLineCount();
499 for (int i=0; i<nLineCount; i++)
501 int nActualLength = GetLineActualLength(i);
502 if (m_nMaxLineLength < nActualLength)
503 m_nMaxLineLength = nActualLength;
506 return m_nMaxLineLength;
509 int CBaseView::GetLineActualLength(int index) const
511 if (m_pViewData == NULL)
512 return 0;
514 return CalculateActualOffset(index, GetLineLength(index));
517 int CBaseView::GetLineLength(int index) const
519 if (m_pViewData == NULL)
520 return 0;
521 if (m_pViewData->GetCount() == 0)
522 return 0;
523 int nLineLength = m_pViewData->GetLine(index).GetLength();
524 ASSERT(nLineLength >= 0);
525 return nLineLength;
528 int CBaseView::GetLineCount() const
530 if (m_pViewData == NULL)
531 return 1;
532 int nLineCount = m_pViewData->GetCount();
533 ASSERT(nLineCount >= 0);
534 return nLineCount;
537 LPCTSTR CBaseView::GetLineChars(int index) const
539 if (m_pViewData == NULL)
540 return 0;
541 if (m_pViewData->GetCount() == 0)
542 return 0;
543 return m_pViewData->GetLine(index);
546 void CBaseView::CheckOtherView()
548 if (m_bOtherDiffChecked)
549 return;
550 // find out what the 'other' file is
551 m_pOtherViewData = NULL;
552 if (this == m_pwndLeft && IsRightViewGood())
553 m_pOtherViewData = m_pwndRight->m_pViewData;
555 if (this == m_pwndRight && IsLeftViewGood())
556 m_pOtherViewData = m_pwndLeft->m_pViewData;
558 m_bOtherDiffChecked = true;
561 CString CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex)
563 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
564 ASSERT(viewData);
566 DiffStates origstate = viewData->GetState(nLineIndex);
568 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
569 int nStartBlock = nLineIndex;
570 int nEndBlock = nLineIndex;
571 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
573 DiffStates state = viewData->GetState(nStartBlock - 1);
574 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
575 origstate = state;
576 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
577 nStartBlock--;
578 else
579 break;
581 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
583 DiffStates state = viewData->GetState(nEndBlock + 1);
584 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
585 origstate = state;
586 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
587 nEndBlock++;
588 else
589 break;
592 CString block;
593 for (int i = nStartBlock; i <= nEndBlock; ++i)
594 block += viewData->GetLine(i);
595 return block;
598 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
600 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
601 CheckOtherView();
602 if (!m_pOtherViewData)
603 return false;
604 if (
605 (m_pViewData->GetState(nLineIndex) == DIFFSTATE_NORMAL) &&
606 (m_pOtherViewData->GetLine(nLineIndex) == m_pViewData->GetLine(nLineIndex))
608 return false;
610 CString mine = GetWhitespaceBlock(m_pViewData, nLineIndex);
611 CString other = GetWhitespaceBlock(m_pOtherViewData, min(nLineIndex, m_pOtherViewData->GetCount() - 1));
612 bIdentical = mine == other;
614 mine.Remove(' ');
615 mine.Remove('\t');
616 mine.Remove('\r');
617 mine.Remove('\n');
618 other.Remove(' ');
619 other.Remove('\t');
620 other.Remove('\r');
621 other.Remove('\n');
623 return (mine == other) && (!mine.IsEmpty());
626 int CBaseView::GetLineNumber(int index) const
628 if (m_pViewData == NULL)
629 return -1;
630 if (m_pViewData->GetLineNumber(index)==DIFF_EMPTYLINENUMBER)
631 return -1;
632 return m_pViewData->GetLineNumber(index);
635 int CBaseView::GetScreenLines()
637 if (m_nScreenLines == -1)
639 SCROLLBARINFO sbi;
640 sbi.cbSize = sizeof(sbi);
641 GetScrollBarInfo(OBJID_HSCROLL, &sbi);
642 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
644 CRect rect;
645 GetClientRect(&rect);
646 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
648 return m_nScreenLines;
651 int CBaseView::GetAllMinScreenLines() const
653 int nLines = 0;
654 if (IsLeftViewGood())
655 nLines = m_pwndLeft->GetScreenLines();
656 if (IsRightViewGood())
657 nLines = (nLines < m_pwndRight->GetScreenLines() ? nLines : m_pwndRight->GetScreenLines());
658 if (IsBottomViewGood())
659 nLines = (nLines < m_pwndBottom->GetScreenLines() ? nLines : m_pwndBottom->GetScreenLines());
660 return nLines;
663 int CBaseView::GetAllLineCount() const
665 int nLines = 0;
666 if (IsLeftViewGood())
667 nLines = m_pwndLeft->GetLineCount();
668 if (IsRightViewGood())
669 nLines = (nLines > m_pwndRight->GetLineCount() ? nLines : m_pwndRight->GetLineCount());
670 if (IsBottomViewGood())
671 nLines = (nLines > m_pwndBottom->GetLineCount() ? nLines : m_pwndBottom->GetLineCount());
672 return nLines;
675 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
677 if (IsLeftViewGood())
678 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
679 if (IsRightViewGood())
680 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
681 if (IsBottomViewGood())
682 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
685 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
687 SCROLLINFO si;
688 si.cbSize = sizeof(si);
689 if (bPositionOnly)
691 si.fMask = SIF_POS;
692 si.nPos = m_nTopLine;
694 else
696 EnableScrollBarCtrl(SB_VERT, TRUE);
697 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
699 m_nTopLine = 0;
700 Invalidate();
702 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
703 si.nMin = 0;
704 si.nMax = GetAllLineCount();
705 si.nPage = GetAllMinScreenLines();
706 si.nPos = m_nTopLine;
708 VERIFY(SetScrollInfo(SB_VERT, &si));
711 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
713 CView::OnVScroll(nSBCode, nPos, pScrollBar);
714 if (m_pwndLeft)
715 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
716 if (m_pwndRight)
717 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
718 if (m_pwndBottom)
719 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
720 if (m_pwndLocator)
721 m_pwndLocator->Invalidate();
724 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
726 // Note we cannot use nPos because of its 16-bit nature
727 SCROLLINFO si;
728 si.cbSize = sizeof(si);
729 si.fMask = SIF_ALL;
730 VERIFY(master->GetScrollInfo(SB_VERT, &si));
732 int nPageLines = GetScreenLines();
733 int nLineCount = GetLineCount();
735 RECT thumbrect;
736 POINT thumbpoint;
737 int nNewTopLine;
739 static LONG textwidth = 0;
740 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
741 switch (nSBCode)
743 case SB_TOP:
744 nNewTopLine = 0;
745 break;
746 case SB_BOTTOM:
747 nNewTopLine = nLineCount - nPageLines + 1;
748 break;
749 case SB_LINEUP:
750 nNewTopLine = m_nTopLine - 1;
751 break;
752 case SB_LINEDOWN:
753 nNewTopLine = m_nTopLine + 1;
754 break;
755 case SB_PAGEUP:
756 nNewTopLine = m_nTopLine - si.nPage + 1;
757 break;
758 case SB_PAGEDOWN:
759 nNewTopLine = m_nTopLine + si.nPage - 1;
760 break;
761 case SB_THUMBPOSITION:
762 m_ScrollTool.Clear();
763 nNewTopLine = si.nTrackPos;
764 textwidth = 0;
765 break;
766 case SB_THUMBTRACK:
767 nNewTopLine = si.nTrackPos;
768 if (GetFocus() == this)
770 GetClientRect(&thumbrect);
771 ClientToScreen(&thumbrect);
772 thumbpoint.x = thumbrect.right;
773 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
774 m_ScrollTool.Init(&thumbpoint);
775 if (textwidth == 0)
777 CString sTemp = sFormat;
778 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
779 textwidth = m_ScrollTool.GetTextWidth(sTemp);
781 thumbpoint.x -= textwidth;
782 int line = GetLineNumber(nNewTopLine);
783 if (line >= 0)
784 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
785 else
786 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
788 break;
789 default:
790 return;
793 if (nNewTopLine < 0)
794 nNewTopLine = 0;
795 if (nNewTopLine >= nLineCount)
796 nNewTopLine = nLineCount - 1;
797 ScrollToLine(nNewTopLine);
800 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
802 if (IsLeftViewGood())
803 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
804 if (IsRightViewGood())
805 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
806 if (IsBottomViewGood())
807 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
810 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
812 SCROLLINFO si;
813 si.cbSize = sizeof(si);
814 if (bPositionOnly)
816 si.fMask = SIF_POS;
817 si.nPos = m_nOffsetChar;
819 else
821 EnableScrollBarCtrl(SB_HORZ, TRUE);
822 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar > 0)
824 m_nOffsetChar = 0;
825 Invalidate();
827 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
828 si.nMin = 0;
829 si.nMax = GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
830 si.nPage = GetAllMinScreenChars();
831 si.nPos = m_nOffsetChar;
833 VERIFY(SetScrollInfo(SB_HORZ, &si));
836 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
838 CView::OnHScroll(nSBCode, nPos, pScrollBar);
839 if (m_pwndLeft)
840 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
841 if (m_pwndRight)
842 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
843 if (m_pwndBottom)
844 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
845 if (m_pwndLocator)
846 m_pwndLocator->Invalidate();
849 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
851 SCROLLINFO si;
852 si.cbSize = sizeof(si);
853 si.fMask = SIF_ALL;
854 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
856 int nPageChars = GetScreenChars();
857 int nMaxLineLength = GetMaxLineLength();
859 int nNewOffset;
860 switch (nSBCode)
862 case SB_LEFT:
863 nNewOffset = 0;
864 break;
865 case SB_BOTTOM:
866 nNewOffset = nMaxLineLength - nPageChars + 1;
867 break;
868 case SB_LINEUP:
869 nNewOffset = m_nOffsetChar - 1;
870 break;
871 case SB_LINEDOWN:
872 nNewOffset = m_nOffsetChar + 1;
873 break;
874 case SB_PAGEUP:
875 nNewOffset = m_nOffsetChar - si.nPage + 1;
876 break;
877 case SB_PAGEDOWN:
878 nNewOffset = m_nOffsetChar + si.nPage - 1;
879 break;
880 case SB_THUMBPOSITION:
881 case SB_THUMBTRACK:
882 nNewOffset = si.nTrackPos;
883 break;
884 default:
885 return;
888 if (nNewOffset >= nMaxLineLength)
889 nNewOffset = nMaxLineLength - 1;
890 if (nNewOffset < 0)
891 nNewOffset = 0;
892 ScrollToChar(nNewOffset, TRUE);
895 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
897 if (m_nOffsetChar != nNewOffsetChar)
899 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
900 m_nOffsetChar = nNewOffsetChar;
901 CRect rcScroll;
902 GetClientRect(&rcScroll);
903 rcScroll.left += GetMarginWidth();
904 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
905 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
906 // update the view header
907 rcScroll.left = 0;
908 rcScroll.top = 0;
909 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
910 InvalidateRect(&rcScroll, FALSE);
911 UpdateWindow();
912 if (bTrackScrollBar)
913 RecalcHorzScrollBar(TRUE);
914 UpdateCaret();
918 void CBaseView::ScrollSide(int delta)
920 int nNewOffset = m_nOffsetChar;
921 nNewOffset += delta;
922 int nMaxLineLength = GetMaxLineLength();
923 if (nNewOffset >= nMaxLineLength)
924 nNewOffset = nMaxLineLength - 1;
925 if (nNewOffset < 0)
926 nNewOffset = 0;
927 ScrollToChar(nNewOffset, TRUE);
928 if (m_pwndLineDiffBar)
929 m_pwndLineDiffBar->Invalidate();
930 UpdateCaret();
933 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
935 if (m_nTopLine != nNewTopLine)
937 if (nNewTopLine < 0)
938 nNewTopLine = 0;
939 int nScrollLines = m_nTopLine - nNewTopLine;
940 m_nTopLine = nNewTopLine;
941 CRect rcScroll;
942 GetClientRect(&rcScroll);
943 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
944 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
945 UpdateWindow();
946 if (bTrackScrollBar)
947 RecalcVertScrollBar(TRUE);
948 UpdateCaret();
953 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
955 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
957 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
959 DiffStates state = m_pViewData->GetState(nLineIndex);
960 HICON icon = NULL;
961 switch (state)
963 case DIFFSTATE_ADDED:
964 case DIFFSTATE_THEIRSADDED:
965 case DIFFSTATE_YOURSADDED:
966 case DIFFSTATE_IDENTICALADDED:
967 case DIFFSTATE_CONFLICTADDED:
968 icon = m_hAddedIcon;
969 break;
970 case DIFFSTATE_REMOVED:
971 case DIFFSTATE_THEIRSREMOVED:
972 case DIFFSTATE_YOURSREMOVED:
973 case DIFFSTATE_IDENTICALREMOVED:
974 icon = m_hRemovedIcon;
975 break;
976 case DIFFSTATE_CONFLICTED:
977 icon = m_hConflictedIcon;
978 break;
979 case DIFFSTATE_CONFLICTED_IGNORED:
980 icon = m_hConflictedIgnoredIcon;
981 break;
982 case DIFFSTATE_EDITED:
983 icon = m_hEditedIcon;
984 break;
985 default:
986 break;
988 bool bIdentical = false;
989 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
991 if (bIdentical)
992 icon = m_hEqualIcon;
993 else
994 icon = m_hWhitespaceBlockIcon;
997 if (icon)
999 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1001 if ((m_bViewLinenumbers)&&(m_nDigits))
1003 int nLineNumber = GetLineNumber(nLineIndex);
1004 if (nLineNumber >= 0)
1006 CString sLinenumberFormat;
1007 CString sLinenumber;
1008 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1009 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1010 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1011 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1013 pdc->SelectObject(GetFont());
1014 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1020 int CBaseView::GetMarginWidth()
1022 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1024 int nWidth = GetCharWidth();
1025 if (m_nDigits <= 0)
1027 int nLength = (int)m_pViewData->GetCount();
1028 // find out how many digits are needed to show the highest line number
1029 int nDigits = 1;
1030 while (nLength / 10)
1032 nDigits++;
1033 nLength /= 10;
1035 m_nDigits = nDigits;
1037 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1039 return MARGINWIDTH;
1042 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1044 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1045 COLORREF crBk, crFg;
1046 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1047 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1048 if (IsBottomViewGood())
1050 pdc->SetBkColor(crBk);
1052 else
1055 if (this == m_pwndRight)
1057 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED, crBk, crFg);
1058 pdc->SetBkColor(crBk);
1060 else
1062 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED, crBk, crFg);
1063 pdc->SetBkColor(crBk);
1066 pdc->FillSolidRect(textrect, crBk);
1068 pdc->SetTextColor(crFg);
1070 pdc->SelectObject(GetFont(FALSE, TRUE, FALSE));
1071 if (IsModified())
1073 if (m_sWindowName.Left(2).Compare(_T("* "))!=0)
1074 m_sWindowName = _T("* ") + m_sWindowName;
1076 else
1078 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
1079 m_sWindowName = m_sWindowName.Mid(2);
1081 CString sViewTitle = m_sWindowName;
1082 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1083 if (nStringLength > rect.Width())
1085 int offset = min(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1087 sViewTitle = m_sWindowName.Mid(offset);
1089 pdc->ExtTextOut(max(rect.left + (rect.Width()-nStringLength)/2, 1),
1090 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1091 if (this->GetFocus() == this)
1092 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1093 else
1094 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1097 void CBaseView::OnDraw(CDC * pDC)
1099 CRect rcClient;
1100 GetClientRect(rcClient);
1102 int nLineCount = GetLineCount();
1103 int nLineHeight = GetLineHeight();
1105 CDC cacheDC;
1106 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1107 if (m_pCacheBitmap == NULL)
1109 m_pCacheBitmap = new CBitmap;
1110 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1112 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1114 DrawHeader(pDC, rcClient);
1116 CRect rcLine;
1117 rcLine = rcClient;
1118 rcLine.top += nLineHeight+HEADERHEIGHT;
1119 rcLine.bottom = rcLine.top + nLineHeight;
1120 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1121 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1123 int nCurrentLine = m_nTopLine;
1124 while (rcLine.top < rcClient.bottom)
1126 if (nCurrentLine < nLineCount)
1128 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1129 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1131 else
1133 DrawMargin(&cacheDC, rcCacheMargin, -1);
1134 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1137 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1139 nCurrentLine ++;
1140 rcLine.OffsetRect(0, nLineHeight);
1143 cacheDC.SelectObject(pOldBitmap);
1144 cacheDC.DeleteDC();
1147 BOOL CBaseView::IsLineRemoved(int nLineIndex)
1149 DiffStates state = DIFFSTATE_UNKNOWN;
1150 if (m_pViewData)
1151 state = m_pViewData->GetState(nLineIndex);
1152 BOOL ret = FALSE;
1153 switch (state)
1155 case DIFFSTATE_REMOVED:
1156 case DIFFSTATE_THEIRSREMOVED:
1157 case DIFFSTATE_YOURSREMOVED:
1158 case DIFFSTATE_IDENTICALREMOVED:
1159 ret = TRUE;
1160 break;
1161 default:
1162 ret = FALSE;
1163 break;
1165 return ret;
1168 bool CBaseView::IsLineConflicted(int nLineIndex)
1170 DiffStates state = DIFFSTATE_UNKNOWN;
1171 if (m_pViewData)
1172 state = m_pViewData->GetState(nLineIndex);
1173 bool ret = false;
1174 switch (state)
1176 case DIFFSTATE_CONFLICTED:
1177 case DIFFSTATE_CONFLICTED_IGNORED:
1178 case DIFFSTATE_CONFLICTEMPTY:
1179 case DIFFSTATE_CONFLICTADDED:
1180 ret = true;
1181 break;
1182 default:
1183 ret = false;
1184 break;
1186 return ret;
1189 COLORREF CBaseView::IntenseColor(long scale, COLORREF col)
1191 // if the color is already dark (gray scale below 127),
1192 // then lighten the color by 'scale', otherwise darken it
1193 int Gray = (((int)GetRValue(col)) + GetGValue(col) + GetBValue(col))/3;
1194 if (Gray > 127)
1196 long red = MulDiv(GetRValue(col),(255-scale),255);
1197 long green = MulDiv(GetGValue(col),(255-scale),255);
1198 long blue = MulDiv(GetBValue(col),(255-scale),255);
1200 return RGB(red, green, blue);
1202 long R = MulDiv(255-GetRValue(col),scale,255)+GetRValue(col);
1203 long G = MulDiv(255-GetGValue(col),scale,255)+GetGValue(col);
1204 long B = MulDiv(255-GetBValue(col),scale,255)+GetBValue(col);
1206 return RGB(R, G, B);
1209 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1211 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1214 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1216 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < m_pViewData->GetCount())))
1217 return;
1219 EOL ending = m_pViewData->GetLineEnding(nLineIndex);
1220 if (m_bIconLFs)
1222 HICON hEndingIcon = NULL;
1223 switch (ending)
1225 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1226 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1227 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1228 default: return;
1230 if (origin.x < (rc.left-GetCharWidth()))
1231 return;
1232 // If EOL style has changed, color end-of-line markers as inline differences.
1234 m_bShowInlineDiff && m_pOtherViewData &&
1235 (nLineIndex < m_pOtherViewData->GetCount()) &&
1236 (ending != EOL_NOENDING) &&
1237 (ending != m_pOtherViewData->GetLineEnding(nLineIndex) &&
1238 (m_pOtherViewData->GetLineEnding(nLineIndex) != EOL_NOENDING))
1241 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1244 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1246 else
1248 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1249 CPen * oldpen = pDC->SelectObject(&pen);
1250 int yMiddle = origin.y + rc.Height()/2;
1251 int xMiddle = origin.x+GetCharWidth()/2;
1252 switch (ending)
1254 case EOL_CR:
1255 // arrow from right to left
1256 pDC->MoveTo(origin.x+GetCharWidth(), yMiddle);
1257 pDC->LineTo(origin.x, yMiddle);
1258 pDC->LineTo(origin.x+4, yMiddle+4);
1259 pDC->MoveTo(origin.x, yMiddle);
1260 pDC->LineTo(origin.x+4, yMiddle-4);
1261 break;
1262 case EOL_CRLF:
1263 // arrow from top to middle+2, then left
1264 pDC->MoveTo(origin.x+GetCharWidth(), rc.top);
1265 pDC->LineTo(origin.x+GetCharWidth(), yMiddle);
1266 pDC->LineTo(origin.x, yMiddle);
1267 pDC->LineTo(origin.x+4, yMiddle+4);
1268 pDC->MoveTo(origin.x, yMiddle);
1269 pDC->LineTo(origin.x+4, yMiddle-4);
1270 break;
1271 case EOL_LF:
1272 // arrow from top to bottom
1273 pDC->MoveTo(xMiddle, rc.top);
1274 pDC->LineTo(xMiddle, rc.bottom-1);
1275 pDC->LineTo(xMiddle+4, rc.bottom-5);
1276 pDC->MoveTo(xMiddle, rc.bottom-1);
1277 pDC->LineTo(xMiddle-4, rc.bottom-5);
1278 break;
1280 pDC->SelectObject(oldpen);
1284 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1286 const int THICKNESS = 2;
1287 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1288 if ((nLineIndex == m_nSelBlockStart) && m_bShowSelection)
1290 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1292 if ((nLineIndex == m_nSelBlockEnd) && m_bShowSelection)
1294 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1298 void CBaseView::DrawText(
1299 CDC * pDC, const CRect &rc, LPCTSTR text, int textlength, int nLineIndex, POINT coords, bool bModified, bool bInlineDiff)
1301 ASSERT(m_pViewData && (nLineIndex < m_pViewData->GetCount()));
1302 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1304 // first suppose the whole line is selected
1305 int selectedStart = 0, selectedEnd = textlength;
1307 if ((m_ptSelectionStartPos.y > nLineIndex) || (m_ptSelectionEndPos.y < nLineIndex)
1308 || ! m_bShowSelection)
1310 // this line has no selected text
1311 selectedStart = textlength;
1313 else if ((m_ptSelectionStartPos.y == nLineIndex) || (m_ptSelectionEndPos.y == nLineIndex))
1315 // the line is partially selected
1316 int xoffs = m_nOffsetChar + (coords.x - GetMarginWidth()) / GetCharWidth();
1317 if (m_ptSelectionStartPos.y == nLineIndex)
1319 // the first line of selection
1320 int nSelectionStartOffset = CalculateActualOffset(m_ptSelectionStartPos.y, m_ptSelectionStartPos.x);
1321 selectedStart = max(min(nSelectionStartOffset - xoffs, textlength), 0);
1324 if (m_ptSelectionEndPos.y == nLineIndex)
1326 // the last line of selection
1327 int nSelectionEndOffset = CalculateActualOffset(m_ptSelectionEndPos.y, m_ptSelectionEndPos.x);
1328 selectedEnd = max(min(nSelectionEndOffset - xoffs, textlength), 0);
1332 COLORREF crBkgnd, crText;
1333 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1334 if (bModified || (diffState == DIFFSTATE_EDITED))
1335 crBkgnd = m_ModifiedBk;
1336 if (bInlineDiff)
1337 crBkgnd = InlineDiffColor(nLineIndex);
1339 pDC->SetBkColor(crBkgnd);
1340 pDC->SetTextColor(crText);
1341 if (selectedStart>=0)
1342 VERIFY(pDC->ExtTextOut(coords.x, coords.y, ETO_CLIPPED, &rc, text, selectedStart, NULL));
1344 long intenseColorScale = m_bFocused ? 70 : 30;
1345 pDC->SetBkColor(IntenseColor(intenseColorScale, crBkgnd));
1346 pDC->SetTextColor(IntenseColor(intenseColorScale, crText));
1347 VERIFY(pDC->ExtTextOut(
1348 coords.x + selectedStart * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1349 text + selectedStart, selectedEnd - selectedStart, NULL));
1351 pDC->SetBkColor(crBkgnd);
1352 pDC->SetTextColor(crText);
1353 if (textlength - selectedEnd >= 0)
1354 VERIFY(pDC->ExtTextOut(
1355 coords.x + selectedEnd * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1356 text + selectedEnd, textlength - selectedEnd, NULL));
1359 bool CBaseView::DrawInlineDiff(CDC *pDC, const CRect &rc, int nLineIndex, const CString &line, CPoint &origin)
1361 if (!m_bShowInlineDiff || line.IsEmpty())
1362 return false;
1363 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
1364 return false;
1366 LPCTSTR pszDiffChars = NULL;
1367 int nDiffLength = 0;
1368 if (m_pOtherViewData)
1370 int index = min(nLineIndex, m_pOtherViewData->GetCount() - 1);
1371 pszDiffChars = m_pOtherViewData->GetLine(index);
1372 nDiffLength = m_pOtherViewData->GetLine(index).GetLength();
1375 if (!pszDiffChars || !*pszDiffChars)
1376 return false;
1378 CString diffline;
1379 ExpandChars(pszDiffChars, 0, nDiffLength, diffline);
1380 svn_diff_t * diff = NULL;
1381 m_svnlinediff.Diff(&diff, line, line.GetLength(), diffline, diffline.GetLength(), m_bInlineWordDiff);
1382 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
1383 return false;
1385 int lineoffset = 0;
1386 std::deque<int> removedPositions;
1387 while (diff)
1389 apr_off_t len = diff->original_length;
1391 CString s;
1392 for (int i = 0; i < len; ++i)
1394 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
1395 lineoffset++;
1397 bool isModified = diff->type == svn_diff__type_diff_modified;
1398 DrawText(pDC, rc, (LPCTSTR)s, s.GetLength(), nLineIndex, origin, true, isModified);
1399 origin.x += pDC->GetTextExtent(s).cx;
1401 if (isModified && (len < diff->modified_length))
1402 removedPositions.push_back(origin.x - 1);
1404 diff = diff->next;
1406 // Draw vertical bars at removed chunks' positions.
1407 for (std::deque<int>::iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
1408 pDC->FillSolidRect(*it, rc.top, 1, rc.Height(), m_InlineRemovedBk);
1409 return true;
1412 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1414 if (nLineIndex >= GetLineCount())
1415 nLineIndex = -1;
1416 ASSERT(nLineIndex >= -1);
1418 if ((nLineIndex == -1) || !m_pViewData)
1420 // Draw line beyond the text
1421 COLORREF crBkgnd, crText;
1422 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1423 pDC->FillSolidRect(rc, crBkgnd);
1424 return;
1427 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1428 COLORREF crBkgnd, crText;
1429 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1431 if (diffState == DIFFSTATE_CONFLICTED)
1433 // conflicted lines are shown without 'text' on them
1434 CRect rect = rc;
1435 pDC->FillSolidRect(rc, crBkgnd);
1436 // now draw some faint text patterns
1437 pDC->SetTextColor(IntenseColor(130, crBkgnd));
1438 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1439 DrawBlockLine(pDC, rc, nLineIndex);
1440 return;
1443 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1444 int nLength = GetLineLength(nLineIndex);
1445 if (nLength == 0)
1447 // Draw the empty line
1448 pDC->FillSolidRect(rc, crBkgnd);
1449 DrawBlockLine(pDC, rc, nLineIndex);
1450 DrawLineEnding(pDC, rc, nLineIndex, origin);
1451 return;
1453 LPCTSTR pszChars = GetLineChars(nLineIndex);
1454 if (pszChars == NULL)
1455 return;
1457 CheckOtherView();
1459 // Draw the line
1461 pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));
1462 CString line;
1463 ExpandChars(pszChars, 0, nLength, line);
1465 int nWidth = rc.right - origin.x;
1466 int savedx = origin.x;
1467 bool bInlineDiffDrawn =
1468 nWidth > 0 && diffState != DIFFSTATE_NORMAL &&
1469 DrawInlineDiff(pDC, rc, nLineIndex, line, origin);
1471 if (!bInlineDiffDrawn)
1473 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);
1474 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);
1477 origin.x = savedx + pDC->GetTextExtent(line).cx;
1479 // draw white space after the end of line
1480 CRect frect = rc;
1481 if (origin.x > frect.left)
1482 frect.left = origin.x;
1483 if (bInlineDiffDrawn)
1484 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1485 if (frect.right > frect.left)
1486 pDC->FillSolidRect(frect, crBkgnd);
1487 // draw the whitespace chars
1488 if (m_bViewWhitespace)
1490 int xpos = 0;
1491 int y = rc.top + (rc.bottom-rc.top)/2;
1493 int nActualOffset = 0;
1494 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1496 if (*pszChars == _T('\t'))
1497 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1498 else
1499 nActualOffset++;
1500 pszChars++;
1502 if (nActualOffset > m_nOffsetChar)
1503 pszChars--;
1505 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1506 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1507 while (*pszChars)
1509 switch (*pszChars)
1511 case _T('\t'):
1513 // draw an arrow
1514 CPen * oldPen = pDC->SelectObject(&pen);
1515 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1516 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1517 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1518 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1519 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1520 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1521 xpos += nSpaces;
1522 pDC->SelectObject(oldPen);
1524 break;
1525 case _T(' '):
1527 // draw a small dot
1528 CPen * oldPen = pDC->SelectObject(&pen2);
1529 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1530 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1531 xpos++;
1532 pDC->SelectObject(oldPen);
1534 break;
1535 default:
1536 xpos++;
1537 break;
1539 pszChars++;
1542 DrawBlockLine(pDC, rc, nLineIndex);
1543 DrawLineEnding(pDC, rc, nLineIndex, origin);
1546 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
1548 if (nCount <= 0)
1550 line = _T("");
1551 return;
1554 int nTabSize = GetTabSize();
1556 int nActualOffset = 0;
1557 for (int i=0; i<nOffset; i++)
1559 if (pszChars[i] == _T('\t'))
1560 nActualOffset += (nTabSize - nActualOffset % nTabSize);
1561 else
1562 nActualOffset ++;
1565 pszChars += nOffset;
1566 int nLength = nCount;
1568 int nTabCount = 0;
1569 for (int i=0; i<nLength; i++)
1571 if (pszChars[i] == _T('\t'))
1572 nTabCount ++;
1575 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1576 int nCurPos = 0;
1577 if (nTabCount > 0 || m_bViewWhitespace)
1579 for (int i=0; i<nLength; i++)
1581 if (pszChars[i] == _T('\t'))
1583 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1584 while (nSpaces > 0)
1586 pszBuf[nCurPos ++] = _T(' ');
1587 nSpaces --;
1590 else
1592 pszBuf[nCurPos] = pszChars[i];
1593 nCurPos ++;
1597 else
1599 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1600 nCurPos = nLength;
1602 pszBuf[nCurPos] = 0;
1603 line.ReleaseBuffer();
1606 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
1608 if ((m_pwndLeft)&&(m_pwndRight))
1610 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1611 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1613 else
1615 if (m_pwndLeft)
1616 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1617 if (m_pwndRight)
1618 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1620 if (m_pwndBottom)
1621 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
1622 if (m_pwndLocator)
1623 m_pwndLocator->Invalidate();
1626 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
1628 //almost the same as ScrollAllToLine, but try to put the line in the
1629 //middle of the view, not on top
1630 int nNewTopLine = nNewLine - GetScreenLines()/2;
1631 if (nNewTopLine < 0)
1632 nNewTopLine = 0;
1633 if (m_pViewData)
1635 if (nNewTopLine >= m_pViewData->GetCount())
1636 nNewTopLine = m_pViewData->GetCount()-1;
1637 if (bAll)
1638 ScrollAllToLine(nNewTopLine);
1639 else
1640 ScrollToLine(nNewTopLine);
1644 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
1646 return TRUE;
1649 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
1651 if (CView::OnCreate(lpCreateStruct) == -1)
1652 return -1;
1654 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
1655 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1656 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1657 m_lfBaseFont.lfHeight = 0;
1658 m_lfBaseFont.lfWeight = FW_NORMAL;
1659 m_lfBaseFont.lfItalic = FALSE;
1660 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
1661 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1662 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1663 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
1664 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
1666 return 0;
1669 void CBaseView::OnDestroy()
1671 CView::OnDestroy();
1672 for (int i=0; i<MAXFONTS; i++)
1674 if (m_apFonts[i] != NULL)
1676 m_apFonts[i]->DeleteObject();
1677 delete m_apFonts[i];
1678 m_apFonts[i] = NULL;
1681 if (m_pCacheBitmap != NULL)
1683 delete m_pCacheBitmap;
1684 m_pCacheBitmap = NULL;
1688 void CBaseView::OnSize(UINT nType, int cx, int cy)
1690 if (m_pCacheBitmap != NULL)
1692 m_pCacheBitmap->DeleteObject();
1693 delete m_pCacheBitmap;
1694 m_pCacheBitmap = NULL;
1696 // make sure the view header is redrawn
1697 CRect rcScroll;
1698 GetClientRect(&rcScroll);
1699 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1700 InvalidateRect(&rcScroll, FALSE);
1702 m_nScreenLines = -1;
1703 m_nScreenChars = -1;
1704 RecalcVertScrollBar();
1705 RecalcHorzScrollBar();
1706 CView::OnSize(nType, cx, cy);
1709 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
1711 if (m_pwndLeft)
1712 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
1713 if (m_pwndRight)
1714 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
1715 if (m_pwndBottom)
1716 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
1717 if (m_pwndLocator)
1718 m_pwndLocator->Invalidate();
1719 return CView::OnMouseWheel(nFlags, zDelta, pt);
1722 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
1724 if (GetKeyState(VK_CONTROL)&0x8000)
1726 // Ctrl-Wheel scrolls sideways
1727 ScrollSide(-zDelta/30);
1729 else
1731 int nLineCount = GetLineCount();
1732 int nTopLine = m_nTopLine;
1733 nTopLine -= (zDelta/30);
1734 if (nTopLine < 0)
1735 nTopLine = 0;
1736 if (nTopLine >= nLineCount)
1737 nTopLine = nLineCount - 1;
1738 ScrollToLine(nTopLine, TRUE);
1742 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1744 if (nHitTest == HTCLIENT)
1746 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
1747 return TRUE;
1749 return CView::OnSetCursor(pWnd, nHitTest, message);
1752 void CBaseView::OnKillFocus(CWnd* pNewWnd)
1754 CView::OnKillFocus(pNewWnd);
1755 m_bFocused = FALSE;
1756 UpdateCaret();
1757 Invalidate();
1760 void CBaseView::OnSetFocus(CWnd* pOldWnd)
1762 CView::OnSetFocus(pOldWnd);
1763 m_bFocused = TRUE;
1764 UpdateCaret();
1765 Invalidate();
1768 int CBaseView::GetLineFromPoint(CPoint point)
1770 ScreenToClient(&point);
1771 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
1774 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)
1776 return false;
1779 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
1781 int nLine = GetLineFromPoint(point);
1783 if (!m_pViewData)
1784 return;
1785 if (m_nSelBlockEnd >= GetLineCount())
1786 m_nSelBlockEnd = GetLineCount()-1;
1787 if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))
1789 int nIndex = nLine - 1;
1790 DiffStates state = m_pViewData->GetState(nIndex);
1791 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1793 // if there's nothing selected, or if the selection is outside the window then
1794 // select the diff block under the cursor.
1795 if (((m_nSelBlockStart<0)&&(m_nSelBlockEnd<0))||
1796 ((m_nSelBlockEnd < m_nTopLine)||(m_nSelBlockStart > m_nTopLine+m_nScreenLines)))
1798 while (nIndex >= 0)
1800 if (nIndex == 0)
1802 nIndex--;
1803 break;
1805 if (state != m_pViewData->GetState(--nIndex))
1806 break;
1808 m_nSelBlockStart = nIndex+1;
1809 while (nIndex < (m_pViewData->GetCount()-1))
1811 if (state != m_pViewData->GetState(++nIndex))
1812 break;
1814 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))
1815 m_nSelBlockEnd = nIndex;
1816 else
1817 m_nSelBlockEnd = nIndex-1;
1818 SetupSelection(m_nSelBlockStart, m_nSelBlockEnd);
1819 m_ptCaretPos.x = 0;
1820 m_ptCaretPos.y = nLine - 1;
1821 UpdateCaret();
1824 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&
1825 (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))
1827 // find a more 'relevant' state in the selection
1828 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)
1830 state = m_pViewData->GetState(i);
1831 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1832 break;
1835 bool bKeepSelection = OnContextMenu(point, nLine, state);
1836 if (! bKeepSelection)
1837 ClearSelection();
1838 RefreshViews();
1842 void CBaseView::RefreshViews()
1844 if (m_pwndLeft)
1846 m_pwndLeft->UpdateStatusBar();
1847 m_pwndLeft->Invalidate();
1849 if (m_pwndRight)
1851 m_pwndRight->UpdateStatusBar();
1852 m_pwndRight->Invalidate();
1854 if (m_pwndBottom)
1856 m_pwndBottom->UpdateStatusBar();
1857 m_pwndBottom->Invalidate();
1859 if (m_pwndLocator)
1860 m_pwndLocator->Invalidate();
1863 void CBaseView::GoToFirstDifference()
1865 m_ptCaretPos.y = 0;
1866 SelectNextBlock(1, false, false);
1869 void CBaseView::HiglightLines(int start, int end /* = -1 */)
1871 ClearSelection();
1872 m_nSelBlockStart = start;
1873 if (end < 0)
1874 end = start;
1875 m_nSelBlockEnd = end;
1876 m_ptCaretPos.x = 0;
1877 m_ptCaretPos.y = start;
1878 UpdateCaret();
1879 Invalidate();
1882 void CBaseView::SetupSelection(int start, int end)
1884 if (IsBottomViewGood())
1886 m_pwndBottom->m_nSelBlockStart = start;
1887 m_pwndBottom->m_nSelBlockEnd = end;
1888 m_pwndBottom->Invalidate();
1890 if (IsLeftViewGood())
1892 m_pwndLeft->m_nSelBlockStart = start;
1893 m_pwndLeft->m_nSelBlockEnd = end;
1894 m_pwndLeft->Invalidate();
1896 if (IsRightViewGood())
1898 m_pwndRight->m_nSelBlockStart = start;
1899 m_pwndRight->m_nSelBlockEnd = end;
1900 m_pwndRight->Invalidate();
1904 void CBaseView::OnMergePreviousconflict()
1906 SelectNextBlock(-1, true);
1909 void CBaseView::OnMergeNextconflict()
1911 SelectNextBlock(1, true);
1914 void CBaseView::OnMergeNextdifference()
1916 SelectNextBlock(1, false);
1919 void CBaseView::OnMergePreviousdifference()
1921 SelectNextBlock(-1, false);
1924 void CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */)
1926 if (! m_pViewData)
1927 return;
1929 if (m_pViewData->GetCount() == 0)
1930 return;
1932 int nCenterPos = m_ptCaretPos.y;
1933 int nLimit = 0;
1934 if (nDirection > 0)
1935 nLimit = m_pViewData->GetCount() - 1;
1937 if (nCenterPos >= m_pViewData->GetCount())
1938 nCenterPos = m_pViewData->GetCount()-1;
1940 if (bSkipEndOfCurrentBlock)
1942 // Find end of current block
1943 DiffStates state = m_pViewData->GetState(nCenterPos);
1944 while ((nCenterPos != nLimit) &&
1945 (m_pViewData->GetState(nCenterPos)==state))
1946 nCenterPos += nDirection;
1949 // Find next diff/conflict block
1950 while (nCenterPos != nLimit)
1952 DiffStates linestate = m_pViewData->GetState(nCenterPos);
1953 if (!bConflict &&
1954 (linestate != DIFFSTATE_NORMAL) &&
1955 (linestate != DIFFSTATE_UNKNOWN))
1956 break;
1957 if (bConflict &&
1958 ((linestate == DIFFSTATE_CONFLICTADDED) ||
1959 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
1960 (linestate == DIFFSTATE_CONFLICTED) ||
1961 (linestate == DIFFSTATE_CONFLICTEMPTY)))
1962 break;
1964 nCenterPos += nDirection;
1967 // Find end of new block
1968 DiffStates state = m_pViewData->GetState(nCenterPos);
1969 int nBlockEnd = nCenterPos;
1970 while ((nBlockEnd != nLimit) &&
1971 (state == m_pViewData->GetState(nBlockEnd + nDirection)))
1972 nBlockEnd += nDirection;
1974 int nTopPos = nCenterPos - (GetScreenLines()/2);
1975 if (nTopPos < 0)
1976 nTopPos = 0;
1978 m_ptCaretPos.x = 0;
1979 m_ptCaretPos.y = nCenterPos;
1980 ClearSelection();
1981 if (nDirection > 0)
1982 SetupSelection(nCenterPos, nBlockEnd);
1983 else
1984 SetupSelection(nBlockEnd, nCenterPos);
1986 ScrollAllToLine(nTopPos, FALSE);
1987 RecalcAllVertScrollBars(TRUE);
1988 m_nCaretGoalPos = 0;
1989 UpdateCaret();
1990 ShowDiffLines(nCenterPos);
1993 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
1995 // need to handle both ANSI and UNICODE versions of the message
1996 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
1997 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
1998 CString strTipText;
1999 UINT nID = (UINT)pNMHDR->idFrom;
2000 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
2001 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
2003 // idFrom is actually the HWND of the tool
2004 nID = ::GetDlgCtrlID((HWND)nID);
2007 if (pNMHDR->idFrom == (UINT)m_hWnd)
2009 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
2011 strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;
2013 else
2015 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2018 else
2019 return FALSE;
2021 *pResult = 0;
2022 if (strTipText.IsEmpty())
2023 return TRUE;
2025 if (pNMHDR->code == TTN_NEEDTEXTA)
2027 pTTTA->lpszText = m_szTip;
2028 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2030 else
2032 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2033 pTTTW->lpszText = m_wszTip;
2036 return TRUE; // message was handled
2040 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2042 CRect rcClient;
2043 GetClientRect(rcClient);
2044 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2045 if (textrect.PtInRect(point))
2047 // inside the header part of the view (showing the filename)
2048 pTI->hwnd = this->m_hWnd;
2049 this->GetClientRect(&pTI->rect);
2050 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2051 pTI->uId = (UINT)m_hWnd;
2052 pTI->lpszText = LPSTR_TEXTCALLBACK;
2054 // we want multi line tooltips
2055 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2056 if (pToolTip->GetSafeHwnd() != NULL)
2058 pToolTip->SetMaxTipWidth(INT_MAX);
2061 return 1;
2063 return -1;
2066 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2068 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2069 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2070 switch (nChar)
2072 case VK_PRIOR:
2074 m_ptCaretPos.y -= GetScreenLines();
2075 m_ptCaretPos.y = max(m_ptCaretPos.y, 0);
2076 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2077 if (bShift)
2078 AdjustSelection();
2079 else
2080 ClearSelection();
2081 UpdateCaret();
2082 EnsureCaretVisible();
2083 ShowDiffLines(m_ptCaretPos.y);
2085 break;
2086 case VK_NEXT:
2088 m_ptCaretPos.y += GetScreenLines();
2089 if (m_ptCaretPos.y >= GetLineCount())
2090 m_ptCaretPos.y = GetLineCount()-1;
2091 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2092 if (bShift)
2093 AdjustSelection();
2094 else
2095 ClearSelection();
2096 UpdateCaret();
2097 EnsureCaretVisible();
2098 ShowDiffLines(m_ptCaretPos.y);
2100 break;
2101 case VK_HOME:
2103 if (bControl)
2105 ScrollAllToLine(0);
2106 m_ptCaretPos.x = 0;
2107 m_ptCaretPos.y = 0;
2108 m_nCaretGoalPos = 0;
2109 if (bShift)
2110 AdjustSelection();
2111 else
2112 ClearSelection();
2113 UpdateCaret();
2115 else
2117 m_ptCaretPos.x = 0;
2118 m_nCaretGoalPos = 0;
2119 if (bShift)
2120 AdjustSelection();
2121 else
2122 ClearSelection();
2123 EnsureCaretVisible();
2124 UpdateCaret();
2127 break;
2128 case VK_END:
2130 if (bControl)
2132 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2133 m_ptCaretPos.y = GetLineCount()-1;
2134 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2135 UpdateGoalPos();
2136 if (bShift)
2137 AdjustSelection();
2138 else
2139 ClearSelection();
2140 UpdateCaret();
2142 else
2144 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2145 UpdateGoalPos();
2146 if (bShift)
2147 AdjustSelection();
2148 else
2149 ClearSelection();
2150 EnsureCaretVisible();
2151 UpdateCaret();
2154 break;
2155 case VK_BACK:
2157 if (m_bCaretHidden)
2158 break;
2160 if (! HasTextSelection()) {
2161 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)
2162 break;
2163 m_ptSelectionEndPos = m_ptCaretPos;
2164 MoveCaretLeft();
2165 m_ptSelectionStartPos = m_ptCaretPos;
2167 RemoveSelectedText();
2169 break;
2170 case VK_DELETE:
2172 if (m_bCaretHidden)
2173 break;
2175 if (! HasTextSelection()) {
2176 if (! MoveCaretRight())
2177 break;
2178 m_ptSelectionEndPos = m_ptCaretPos;
2179 MoveCaretLeft();
2180 m_ptSelectionStartPos = m_ptCaretPos;
2182 RemoveSelectedText();
2184 break;
2186 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2189 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2191 int nClickedLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2192 nClickedLine--; //we need the index
2193 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2195 m_ptCaretPos.y = nClickedLine;
2196 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2197 UpdateGoalPos();
2199 if (nFlags & MK_SHIFT)
2200 AdjustSelection();
2201 else
2203 ClearSelection();
2204 SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);
2207 UpdateCaret();
2209 Invalidate();
2212 CView::OnLButtonDown(nFlags, point);
2215 void CBaseView::OnEditCopy()
2217 if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))
2218 return;
2219 // first store the selected lines in one CString
2220 CString sCopyData;
2221 for (int i=m_ptSelectionStartPos.y; i<=m_ptSelectionEndPos.y; i++)
2223 switch (m_pViewData->GetState(i))
2225 case DIFFSTATE_EMPTY:
2226 break;
2227 case DIFFSTATE_UNKNOWN:
2228 case DIFFSTATE_NORMAL:
2229 case DIFFSTATE_REMOVED:
2230 case DIFFSTATE_REMOVEDWHITESPACE:
2231 case DIFFSTATE_ADDED:
2232 case DIFFSTATE_ADDEDWHITESPACE:
2233 case DIFFSTATE_WHITESPACE:
2234 case DIFFSTATE_WHITESPACE_DIFF:
2235 case DIFFSTATE_CONFLICTED:
2236 case DIFFSTATE_CONFLICTED_IGNORED:
2237 case DIFFSTATE_CONFLICTADDED:
2238 case DIFFSTATE_CONFLICTEMPTY:
2239 case DIFFSTATE_CONFLICTRESOLVED:
2240 case DIFFSTATE_IDENTICALREMOVED:
2241 case DIFFSTATE_IDENTICALADDED:
2242 case DIFFSTATE_THEIRSREMOVED:
2243 case DIFFSTATE_THEIRSADDED:
2244 case DIFFSTATE_YOURSREMOVED:
2245 case DIFFSTATE_YOURSADDED:
2246 case DIFFSTATE_EDITED:
2247 sCopyData += m_pViewData->GetLine(i);
2248 sCopyData += _T("\r\n");
2249 break;
2252 // remove the last \r\n
2253 sCopyData = sCopyData.Left(sCopyData.GetLength()-2);
2254 // remove the non-selected chars from the first line
2255 sCopyData = sCopyData.Mid(m_ptSelectionStartPos.x);
2256 // remove the non-selected chars from the last line
2257 int lastLinePos = sCopyData.ReverseFind('\n');
2258 lastLinePos += 1;
2259 if (lastLinePos == 0)
2260 lastLinePos -= m_ptSelectionStartPos.x;
2261 sCopyData = sCopyData.Left(lastLinePos+m_ptSelectionEndPos.x);
2262 if (!sCopyData.IsEmpty())
2264 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
2268 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
2270 if (m_pMainFrame->m_nMoveMovesToIgnore > 0) {
2271 --m_pMainFrame->m_nMoveMovesToIgnore;
2272 CView::OnMouseMove(nFlags, point);
2273 return;
2276 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2277 nMouseLine--; //we need the index
2278 if (nMouseLine < -1)
2280 nMouseLine = -1;
2282 ShowDiffLines(nMouseLine);
2284 KillTimer(IDT_SCROLLTIMER);
2285 if (nFlags & MK_LBUTTON)
2287 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2288 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2289 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2290 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&
2291 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
2293 m_ptCaretPos.y = nMouseLine;
2294 m_ptCaretPos.x = charIndex;
2295 UpdateGoalPos();
2296 AdjustSelection();
2297 UpdateCaret();
2298 Invalidate();
2299 UpdateWindow();
2301 if (nMouseLine < m_nTopLine)
2303 ScrollToLine(m_nTopLine-1, TRUE);
2304 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2306 if (nMouseLine >= m_nTopLine + GetScreenLines())
2308 ScrollToLine(m_nTopLine+1, TRUE);
2309 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2311 if (charIndex <= m_nOffsetChar)
2313 ScrollSide(-1);
2314 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2316 if (charIndex >= (GetScreenChars()+m_nOffsetChar))
2318 ScrollSide(1);
2319 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2323 if (!m_bMouseWithin)
2325 m_bMouseWithin = TRUE;
2326 TRACKMOUSEEVENT tme;
2327 tme.cbSize = sizeof(TRACKMOUSEEVENT);
2328 tme.dwFlags = TME_LEAVE;
2329 tme.hwndTrack = m_hWnd;
2330 _TrackMouseEvent(&tme);
2333 CView::OnMouseMove(nFlags, point);
2336 void CBaseView::OnMouseLeave()
2338 ShowDiffLines(-1);
2339 m_bMouseWithin = FALSE;
2340 KillTimer(IDT_SCROLLTIMER);
2341 CView::OnMouseLeave();
2344 void CBaseView::OnTimer(UINT_PTR nIDEvent)
2346 if (nIDEvent == IDT_SCROLLTIMER)
2348 POINT point;
2349 GetCursorPos(&point);
2350 ScreenToClient(&point);
2351 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2352 nMouseLine--; //we need the index
2353 if (nMouseLine < -1)
2355 nMouseLine = -1;
2357 if (GetKeyState(VK_LBUTTON)&0x8000)
2359 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2360 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2361 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2362 if (nMouseLine < m_nTopLine)
2364 ScrollToLine(m_nTopLine-1, TRUE);
2365 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2367 if (nMouseLine >= m_nTopLine + GetScreenLines())
2369 ScrollToLine(m_nTopLine+1, TRUE);
2370 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2372 if (charIndex <= m_nOffsetChar)
2374 ScrollSide(-1);
2375 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2377 if (charIndex >= GetScreenChars())
2379 ScrollSide(1);
2380 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2386 CView::OnTimer(nIDEvent);
2389 void CBaseView::SelectLines(int nLine1, int nLine2)
2391 if (nLine2 == -1)
2392 nLine2 = nLine1;
2393 m_nSelBlockStart = nLine1;
2394 m_nSelBlockEnd = nLine2;
2395 Invalidate();
2398 void CBaseView::ShowDiffLines(int nLine)
2400 if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))
2402 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))
2404 nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);
2405 nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);
2407 if (nLine >= 0)
2409 if (nLine != m_nMouseLine)
2411 m_nMouseLine = nLine;
2412 if (nLine >= GetLineCount())
2413 nLine = -1;
2414 m_pwndLineDiffBar->ShowLines(nLine);
2419 else
2421 m_pwndLineDiffBar->ShowLines(nLine);
2425 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2427 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2428 return;
2429 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2431 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2432 m_pwndBottom->m_pViewData->SetLine(i, m_pwndLeft->m_pViewData->GetLine(i));
2433 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2434 m_pwndBottom->m_pViewData->SetState(i, m_pwndLeft->m_pViewData->GetState(i));
2435 m_pwndBottom->m_pViewData->SetLineEnding(i, m_pwndBottom->lineendings);
2436 if (m_pwndBottom->IsLineConflicted(i))
2438 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2439 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2440 else
2441 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2443 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2446 // your block is done, now insert their block
2447 int index = m_nSelBlockEnd+1;
2448 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2450 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2451 m_pwndBottom->m_pViewData->InsertData(index, m_pwndRight->m_pViewData->GetData(i));
2452 if (m_pwndBottom->IsLineConflicted(index))
2454 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2455 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2456 else
2457 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2459 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2460 index++;
2462 // adjust line numbers
2463 for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)
2465 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2466 if (oldline >= 0)
2467 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2470 // now insert an empty block in both yours and theirs
2471 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2473 rightstate.addedlines.push_back(m_nSelBlockStart);
2474 m_pwndRight->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2475 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2476 leftstate.addedlines.push_back(m_nSelBlockEnd+1);
2478 RecalcAllVertScrollBars();
2479 m_pwndBottom->SetModified();
2480 m_pwndLeft->SetModified();
2481 m_pwndRight->SetModified();
2484 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2486 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2487 return;
2488 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2490 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2491 m_pwndBottom->m_pViewData->SetLine(i, m_pwndRight->m_pViewData->GetLine(i));
2492 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2493 m_pwndBottom->m_pViewData->SetState(i, m_pwndRight->m_pViewData->GetState(i));
2494 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2495 m_pwndBottom->m_pViewData->SetLineEnding(i, m_pwndBottom->lineendings);
2496 if (m_pwndBottom->IsLineConflicted(i))
2498 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2499 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2500 else
2501 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2503 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2506 // your block is done, now insert their block
2507 int index = m_nSelBlockEnd+1;
2508 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2510 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2511 m_pwndBottom->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2512 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2513 if (m_pwndBottom->IsLineConflicted(index))
2515 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2516 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2517 else
2518 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2520 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2521 index++;
2523 // adjust line numbers
2524 for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)
2526 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2527 if (oldline >= 0)
2528 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2531 // now insert an empty block in both yours and theirs
2532 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2534 leftstate.addedlines.push_back(m_nSelBlockStart);
2535 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2536 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2537 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2540 RecalcAllVertScrollBars();
2541 m_pwndBottom->SetModified();
2542 m_pwndLeft->SetModified();
2543 m_pwndRight->SetModified();
2546 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)
2548 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2549 return;
2550 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2552 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2553 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2556 // your block is done, now insert their block
2557 int index = m_nSelBlockEnd+1;
2558 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2560 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2561 m_pwndRight->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2562 m_pwndRight->m_pViewData->SetState(index++, DIFFSTATE_THEIRSADDED);
2564 // adjust line numbers
2565 index--;
2566 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2568 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2569 if (oldline >= 0)
2570 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2573 // now insert an empty block in the left view
2574 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2576 leftstate.addedlines.push_back(m_nSelBlockStart);
2577 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2579 RecalcAllVertScrollBars();
2580 m_pwndLeft->SetModified();
2581 m_pwndRight->SetModified();
2584 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)
2586 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2587 return;
2588 // get line number from just before the block
2589 long linenumber = 0;
2590 if (m_nSelBlockStart > 0)
2591 linenumber = m_pwndRight->m_pViewData->GetLineNumber(m_nSelBlockStart-1);
2592 linenumber++;
2593 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2595 rightstate.addedlines.push_back(m_nSelBlockStart);
2596 m_pwndRight->m_pViewData->InsertData(i, m_pwndLeft->m_pViewData->GetLine(i), DIFFSTATE_THEIRSADDED, linenumber++, m_pwndLeft->m_pViewData->GetLineEnding(i));
2598 // adjust line numbers
2599 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2601 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2602 if (oldline >= 0)
2603 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);
2606 // now insert an empty block in left view
2607 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2609 leftstate.addedlines.push_back(m_nSelBlockEnd + 1);
2610 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd + 1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2612 RecalcAllVertScrollBars();
2613 m_pwndLeft->SetModified();
2614 m_pwndRight->SetModified();
2617 void CBaseView::UpdateCaret()
2619 if (m_ptCaretPos.y >= GetLineCount())
2620 m_ptCaretPos.y = GetLineCount()-1;
2621 if (m_ptCaretPos.y < 0)
2622 m_ptCaretPos.y = 0;
2623 if (m_ptCaretPos.x > GetLineLength(m_ptCaretPos.y))
2624 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2625 if (m_ptCaretPos.x < 0)
2626 m_ptCaretPos.x = 0;
2628 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2630 if (m_bFocused && !m_bCaretHidden &&
2631 m_ptCaretPos.y >= m_nTopLine &&
2632 m_ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
2633 nCaretOffset >= m_nOffsetChar &&
2634 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
2636 CreateSolidCaret(2, GetLineHeight());
2637 SetCaretPos(TextToClient(m_ptCaretPos));
2638 ShowCaret();
2640 else
2642 HideCaret();
2646 void CBaseView::EnsureCaretVisible()
2648 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2650 if (m_ptCaretPos.y < m_nTopLine)
2651 ScrollAllToLine(m_ptCaretPos.y);
2652 if (m_ptCaretPos.y >= (m_nTopLine+GetScreenLines()))
2653 ScrollAllToLine(m_ptCaretPos.y-GetScreenLines()+1);
2654 if (nCaretOffset < m_nOffsetChar)
2655 ScrollToChar(nCaretOffset);
2656 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
2657 ScrollToChar(nCaretOffset-GetScreenChars()+1);
2660 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const
2662 int nLength = GetLineLength(nLineIndex);
2663 ASSERT(nCharIndex >= 0);
2664 if (nCharIndex > nLength)
2665 nCharIndex = nLength;
2666 LPCTSTR pszChars = GetLineChars(nLineIndex);
2667 int nOffset = 0;
2668 int nTabSize = GetTabSize();
2669 for (int I = 0; I < nCharIndex; I ++)
2671 if (pszChars[I] == _T('\t'))
2672 nOffset += (nTabSize - nOffset % nTabSize);
2673 else
2674 nOffset++;
2676 return nOffset;
2679 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const
2681 int nLength = GetLineLength(nLineIndex);
2682 LPCTSTR pszLine = GetLineChars(nLineIndex);
2683 int nIndex = 0;
2684 int nOffset = 0;
2685 int nTabSize = GetTabSize();
2686 while (nOffset < nActualOffset && nIndex < nLength)
2688 if (pszLine[nIndex] == _T('\t'))
2689 nOffset += (nTabSize - nOffset % nTabSize);
2690 else
2691 ++nOffset;
2692 ++nIndex;
2694 return nIndex;
2697 POINT CBaseView::TextToClient(const POINT& point)
2699 POINT pt;
2700 pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());
2701 pt.x = CalculateActualOffset(point.y, point.x);
2703 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();
2704 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
2705 return pt;
2708 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
2710 CView::OnChar(nChar, nRepCnt, nFlags);
2712 if (m_bCaretHidden)
2713 return;
2715 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
2716 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
2717 return;
2719 if ((nChar > 31)||(nChar == VK_TAB))
2721 RemoveSelectedText();
2722 AddUndoLine(m_ptCaretPos.y);
2723 CString sLine = GetLineChars(m_ptCaretPos.y);
2724 sLine.Insert(m_ptCaretPos.x, (wchar_t)nChar);
2725 m_pViewData->SetLine(m_ptCaretPos.y, sLine);
2726 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);
2727 if (m_pViewData->GetLineEnding(m_ptCaretPos.y) == EOL_NOENDING && m_pViewData->GetCount() - 1 != m_ptCaretPos.y)
2728 m_pViewData->SetLineEnding(m_ptCaretPos.y, lineendings);
2729 m_ptCaretPos.x++;
2730 UpdateGoalPos();
2732 else if (nChar == 10)
2734 AddUndoLine(m_ptCaretPos.y);
2735 EOL eol = m_pViewData->GetLineEnding(m_ptCaretPos.y);
2736 EOL newEOL = EOL_CRLF;
2737 switch (eol) {
2738 case EOL_CRLF:
2739 newEOL = EOL_CR;
2740 break;
2741 case EOL_CR:
2742 newEOL = EOL_LF;
2743 break;
2744 case EOL_LF:
2745 newEOL = (m_pViewData->GetCount() - 1 == m_ptCaretPos.y && !m_pViewData->GetLine(m_ptCaretPos.y).IsEmpty()) ? EOL_NOENDING : EOL_CRLF;
2746 break;
2748 m_pViewData->SetLineEnding(m_ptCaretPos.y, newEOL);
2749 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);
2750 UpdateGoalPos();
2752 else if (nChar == VK_RETURN)
2754 // insert a new, fresh and empty line below the cursor
2755 RemoveSelectedText();
2756 AddUndoLine(m_ptCaretPos.y, true);
2757 EOL eOriginalEnding = m_pViewData->GetLineEnding(m_ptCaretPos.y);
2758 if (m_pViewData->GetCount() - 2 == m_ptCaretPos.y && eOriginalEnding == EOL_NOENDING)
2759 m_pViewData->SetLineEnding(m_ptCaretPos.y, lineendings);
2760 // move the cursor to the new line
2761 m_ptCaretPos.y++;
2762 m_ptCaretPos.x = 0;
2763 if (m_pViewData->GetCount() - 1 == m_ptCaretPos.y)
2764 m_pViewData->SetLineEnding(m_ptCaretPos.y, eOriginalEnding);
2765 UpdateGoalPos();
2767 else
2768 return; // Unknown control character -- ignore it.
2769 ClearSelection();
2770 EnsureCaretVisible();
2771 UpdateCaret();
2772 SetModified(true);
2773 Invalidate(FALSE);
2776 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)
2778 viewstate leftstate;
2779 viewstate rightstate;
2780 viewstate bottomstate;
2781 leftstate.AddLineFormView(m_pwndLeft, nLine, bAddEmptyLine);
2782 rightstate.AddLineFormView(m_pwndRight, nLine, bAddEmptyLine);
2783 bottomstate.AddLineFormView(m_pwndBottom, nLine, bAddEmptyLine);
2784 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2787 void CBaseView::AddEmptyLine(int nLineIndex)
2789 if (m_pViewData == NULL)
2790 return;
2791 if (!m_bCaretHidden)
2793 CString sPartLine = GetLineChars(nLineIndex);
2794 m_pViewData->SetLine(nLineIndex, sPartLine.Left(m_ptCaretPos.x));
2795 sPartLine = sPartLine.Mid(m_ptCaretPos.x);
2796 m_pViewData->InsertData(nLineIndex+1, sPartLine, DIFFSTATE_EDITED, -1, lineendings);
2798 else
2799 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, lineendings);
2800 Invalidate(FALSE);
2803 void CBaseView::RemoveLine(int nLineIndex)
2805 if (m_pViewData == NULL)
2806 return;
2807 m_pViewData->RemoveData(nLineIndex);
2808 if (m_ptCaretPos.y >= GetLineCount())
2809 m_ptCaretPos.y = GetLineCount()-1;
2810 Invalidate(FALSE);
2813 void CBaseView::RemoveSelectedText()
2815 if (m_pViewData == NULL)
2816 return;
2817 if (!HasTextSelection())
2818 return;
2820 viewstate rightstate;
2821 viewstate bottomstate;
2822 viewstate leftstate;
2823 std::vector<LONG> linestoremove;
2824 for (LONG i = m_ptSelectionStartPos.y; i <= m_ptSelectionEndPos.y; ++i)
2826 if (i == m_ptSelectionStartPos.y)
2828 CString sLine = GetLineChars(m_ptSelectionStartPos.y);
2829 CString newLine;
2830 if (i == m_ptSelectionStartPos.y)
2832 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2834 leftstate.difflines[i] = m_pwndLeft->m_pViewData->GetLine(i);
2835 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2837 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2839 rightstate.difflines[i] = m_pwndRight->m_pViewData->GetLine(i);
2840 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2842 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2844 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2845 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2847 newLine = sLine.Left(m_ptSelectionStartPos.x);
2848 sLine = GetLineChars(m_ptSelectionEndPos.y);
2849 newLine = newLine + sLine.Mid(m_ptSelectionEndPos.x);
2851 m_pViewData->SetLine(i, newLine);
2852 m_pViewData->SetState(i, DIFFSTATE_EDITED);
2853 SetModified();
2855 else
2857 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2859 leftstate.removedlines[i] = m_pwndLeft->m_pViewData->GetData(i);
2861 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2863 rightstate.removedlines[i] = m_pwndRight->m_pViewData->GetData(i);
2865 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2867 bottomstate.removedlines[i] = m_pwndBottom->m_pViewData->GetData(i);
2869 linestoremove.push_back(i);
2872 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2873 // remove the lines at the end, to avoid problems with line indexes
2874 if (!linestoremove.empty())
2876 std::vector<LONG>::const_iterator it = linestoremove.begin();
2877 int nLineToRemove = *it;
2878 for ( ; it != linestoremove.end(); ++it)
2880 if (m_pwndLeft)
2881 m_pwndLeft->RemoveLine(nLineToRemove);
2882 if (m_pwndRight)
2883 m_pwndRight->RemoveLine(nLineToRemove);
2884 if (m_pwndBottom)
2885 m_pwndBottom->RemoveLine(nLineToRemove);
2886 SetModified();
2889 m_ptCaretPos = m_ptSelectionStartPos;
2890 UpdateGoalPos();
2891 ClearSelection();
2892 UpdateCaret();
2893 EnsureCaretVisible();
2894 Invalidate(FALSE);
2897 void CBaseView::PasteText()
2899 if (!OpenClipboard())
2900 return;
2902 CString sClipboardText;
2903 HGLOBAL hglb = GetClipboardData(CF_TEXT);
2904 if (hglb)
2906 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
2907 sClipboardText = CString(lpstr);
2908 GlobalUnlock(hglb);
2910 hglb = GetClipboardData(CF_UNICODETEXT);
2911 if (hglb)
2913 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
2914 sClipboardText = lpstr;
2915 GlobalUnlock(hglb);
2917 CloseClipboard();
2919 if (sClipboardText.IsEmpty())
2920 return;
2922 sClipboardText.Replace(_T("\r\n"), _T("\r"));
2923 sClipboardText.Replace('\n', '\r');
2924 // We want to undo the insertion in a single step.
2925 CUndo::GetInstance().BeginGrouping();
2926 // use the easy way to insert text:
2927 // insert char by char, using the OnChar() method
2928 for (int i=0; i<sClipboardText.GetLength(); ++i)
2930 OnChar(sClipboardText[i], 0, 0);
2932 CUndo::GetInstance().EndGrouping();
2935 void CBaseView::OnCaretDown()
2937 m_ptCaretPos.y++;
2938 m_ptCaretPos.y = min(m_ptCaretPos.y, GetLineCount()-1);
2939 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2940 if (GetKeyState(VK_SHIFT)&0x8000)
2941 AdjustSelection();
2942 else
2943 ClearSelection();
2944 UpdateCaret();
2945 EnsureCaretVisible();
2946 ShowDiffLines(m_ptCaretPos.y);
2949 bool CBaseView::MoveCaretLeft()
2951 if (m_ptCaretPos.x == 0)
2953 if (m_ptCaretPos.y > 0)
2955 --m_ptCaretPos.y;
2956 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2958 else
2959 return false;
2961 else
2962 --m_ptCaretPos.x;
2964 UpdateGoalPos();
2965 return true;
2968 bool CBaseView::MoveCaretRight()
2970 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
2972 if (m_ptCaretPos.y < (GetLineCount() - 1))
2974 ++m_ptCaretPos.y;
2975 m_ptCaretPos.x = 0;
2977 else
2978 return false;
2980 else
2981 ++m_ptCaretPos.x;
2983 UpdateGoalPos();
2984 return true;
2987 void CBaseView::UpdateGoalPos()
2989 m_nCaretGoalPos = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2992 void CBaseView::OnCaretLeft()
2994 MoveCaretLeft();
2995 if (GetKeyState(VK_SHIFT)&0x8000)
2996 AdjustSelection();
2997 else
2998 ClearSelection();
2999 EnsureCaretVisible();
3000 UpdateCaret();
3003 void CBaseView::OnCaretRight()
3005 MoveCaretRight();
3006 if (GetKeyState(VK_SHIFT)&0x8000)
3007 AdjustSelection();
3008 else
3009 ClearSelection();
3010 EnsureCaretVisible();
3011 UpdateCaret();
3014 void CBaseView::OnCaretUp()
3016 m_ptCaretPos.y--;
3017 m_ptCaretPos.y = max(0, m_ptCaretPos.y);
3018 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
3019 if (GetKeyState(VK_SHIFT)&0x8000)
3020 AdjustSelection();
3021 else
3022 ClearSelection();
3023 UpdateCaret();
3024 EnsureCaretVisible();
3025 ShowDiffLines(m_ptCaretPos.y);
3028 bool CBaseView::IsWordSeparator(wchar_t ch) const
3030 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3033 bool CBaseView::IsCaretAtWordBoundary() const
3035 LPCTSTR line = GetLineChars(m_ptCaretPos.y);
3036 if (!*line)
3037 return false; // no boundary at the empty lines
3038 if (m_ptCaretPos.x == 0)
3039 return !IsWordSeparator(line[m_ptCaretPos.x]);
3040 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
3041 return !IsWordSeparator(line[m_ptCaretPos.x - 1]);
3042 return
3043 IsWordSeparator(line[m_ptCaretPos.x]) !=
3044 IsWordSeparator(line[m_ptCaretPos.x - 1]);
3047 void CBaseView::OnCaretWordleft()
3049 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3052 if (GetKeyState(VK_SHIFT)&0x8000)
3053 AdjustSelection();
3054 else
3055 ClearSelection();
3056 EnsureCaretVisible();
3057 UpdateCaret();
3060 void CBaseView::OnCaretWordright()
3062 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3065 if (GetKeyState(VK_SHIFT)&0x8000)
3066 AdjustSelection();
3067 else
3068 ClearSelection();
3069 EnsureCaretVisible();
3070 UpdateCaret();
3073 void CBaseView::ClearCurrentSelection()
3075 m_ptSelectionStartPos = m_ptCaretPos;
3076 m_ptSelectionEndPos = m_ptCaretPos;
3077 m_ptSelectionOrigin = m_ptCaretPos;
3078 m_nSelBlockStart = -1;
3079 m_nSelBlockEnd = -1;
3080 Invalidate(FALSE);
3083 void CBaseView::ClearSelection()
3085 if (m_pwndLeft)
3086 m_pwndLeft->ClearCurrentSelection();
3087 if (m_pwndRight)
3088 m_pwndRight->ClearCurrentSelection();
3089 if (m_pwndBottom)
3090 m_pwndBottom->ClearCurrentSelection();
3093 void CBaseView::AdjustSelection()
3095 if ((m_ptCaretPos.y < m_ptSelectionOrigin.y) ||
3096 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x <= m_ptSelectionOrigin.x))
3098 m_ptSelectionStartPos = m_ptCaretPos;
3099 m_ptSelectionEndPos = m_ptSelectionOrigin;
3102 if ((m_ptCaretPos.y > m_ptSelectionOrigin.y) ||
3103 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x >= m_ptSelectionOrigin.x))
3105 m_ptSelectionStartPos = m_ptSelectionOrigin;
3106 m_ptSelectionEndPos = m_ptCaretPos;
3109 SetupSelection(min(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y), max(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y));
3111 Invalidate(FALSE);
3114 void CBaseView::OnEditCut()
3116 if (!m_bCaretHidden)
3118 OnEditCopy();
3119 RemoveSelectedText();
3123 void CBaseView::OnEditPaste()
3125 if (!m_bCaretHidden)
3127 PasteText();
3131 void CBaseView::OnEditSelectall()
3133 int nCount = GetLineCount();
3134 SetupSelection(0, nCount);
3135 m_ptSelectionStartPos.x = 0;
3136 m_ptSelectionStartPos.y = 0;
3138 m_ptSelectionEndPos.y = nCount-1;
3139 CString sLine = GetLineChars(nCount-1);
3140 m_ptSelectionEndPos.x = sLine.GetLength();
3142 UpdateWindow();