1 /////////////////////////////////////////////////////////////////////////////
3 // WinMerge: An interactive diff/merge utility
4 // Copyright (C) 1997 Dean P. Grimm
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (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
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
23 * @file LocationView.cpp
25 * @brief Implementation file for CLocationView
31 #include "LocationView.h"
34 #include "OptionsMgr.h"
35 #include "MergeEditView.h"
38 #include "OptionsDef.h"
41 #include "SyntaxColors.h"
46 static char THIS_FILE[] = __FILE__;
51 /** @brief Size of empty frame above and below bars (in pixels). */
52 static const int Y_OFFSET = 5;
53 /** @brief Size of y-margin for visible area indicator (in pixels). */
54 static const long INDICATOR_MARGIN = 2;
55 /** @brief Max pixels in view per line in file. */
56 static const double MAX_LINEPIX = 4.0;
57 /** @brief Top of difference marker, relative to difference start. */
58 static const int DIFFMARKER_TOP = 3;
59 /** @brief Bottom of difference marker, relative to difference start. */
60 static const int DIFFMARKER_BOTTOM = 3;
61 /** @brief Width of difference marker. */
62 static const int DIFFMARKER_WIDTH = 6;
63 /** @brief Minimum height of the visible area indicator */
64 static const int INDICATOR_MIN_HEIGHT = 2;
67 * @brief Bars in location pane
71 BAR_NONE = -1, /**< No bar in given coords */
72 BAR_0, /**< first bar in given coords */
73 BAR_1, /**< second bar in given coords */
74 BAR_2, /**< third side bar in given coords */
75 BAR_YAREA, /**< Y-Coord in bar area */
78 const COLORREF clrBackground = RGB(0xe4, 0xe4, 0xf4);
80 /////////////////////////////////////////////////////////////////////////////
83 IMPLEMENT_DYNCREATE(CLocationView, CView)
86 CLocationView::CLocationView()
89 // MOVEDLINE_LIST m_movedLines; //*< List of moved block connecting lines */
90 , m_hwndFrame(nullptr)
91 , m_pSavedBackgroundBitmap(nullptr)
93 , m_bRecalculateBlocks(TRUE) // calculate for the first time
95 // NB: set m_bIgnoreTrivials to false to see trivial diffs in the LocationView
96 // There is no GUI to do this
98 SetConnectMovedBlocks(GetOptionsMgr()->GetInt(OPT_CONNECT_MOVED_BLOCKS));
100 std::fill_n(m_view, countof(m_view), static_cast<CMergeEditView *>(NULL));
101 std::fill_n(m_nSubLineCount, countof(m_view), 0);
104 CLocationView::~CLocationView()
108 BEGIN_MESSAGE_MAP(CLocationView, CView)
109 //{{AFX_MSG_MAP(CLocationView)
113 ON_WM_LBUTTONDBLCLK()
120 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
121 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
122 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
123 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
128 void CLocationView::SetConnectMovedBlocks(int displayMovedBlocks)
130 if (m_displayMovedBlocks == displayMovedBlocks)
133 GetOptionsMgr()->SaveOption(OPT_CONNECT_MOVED_BLOCKS, displayMovedBlocks);
134 m_displayMovedBlocks = displayMovedBlocks;
135 if (this->GetSafeHwnd() != NULL)
136 if (IsWindowVisible())
140 /////////////////////////////////////////////////////////////////////////////
141 // CLocationView diagnostics
143 CMergeDoc* CLocationView::GetDocument() // non-debug version is inline
145 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
146 return (CMergeDoc*)m_pDocument;
150 /////////////////////////////////////////////////////////////////////////////
151 // CLocationView message handlers
154 * @brief Force recalculation and update of location pane.
155 * This method forces location pane to first recalculate its data and
156 * then repaint itself. This method bypasses location pane's caching
159 void CLocationView::ForceRecalculate()
161 m_bRecalculateBlocks = TRUE;
166 * @brief Update view.
168 void CLocationView::OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint )
170 UNREFERENCED_PARAMETER(pSender);
171 UNREFERENCED_PARAMETER(lHint);
172 CMergeDoc* pDoc = GetDocument();
173 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
175 m_view[pane] = pDoc->GetView(pane);
177 // Give pointer to MergeEditView
178 m_view[pane]->SetLocationView(this);
181 m_bRecalculateBlocks = TRUE;
186 * @brief Override for CMemDC to work.
188 BOOL CLocationView::OnEraseBkgnd(CDC* pDC)
193 void CLocationView::OnSetFocus(CWnd* pOldWnd)
195 if (pOldWnd && pOldWnd->IsChild(this))
196 m_view[0]->SetFocus();
202 * @brief Draw custom (non-white) background.
203 * @param [in] pDC Pointer to draw context.
205 void CLocationView::DrawBackground(CDC* pDC)
207 // Set brush to desired background color
208 CBrush backBrush(clrBackground);
211 CBrush* pOldBrush = pDC->SelectObject(&backBrush);
214 pDC->GetClipBox(&rect); // Erase the area needed
216 pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
218 pDC->SelectObject(pOldBrush);
222 * @brief Calculate bar coordinates and scaling factors.
224 void CLocationView::CalculateBars()
226 CMergeDoc *pDoc = GetDocument();
229 const int w = rc.Width() / (pDoc->m_nBuffers * 2);
230 const int margin = (rc.Width() - w * pDoc->m_nBuffers) / (pDoc->m_nBuffers + 1);
232 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
234 m_bar[pane].left = pane * (w + margin) + margin;
235 m_bar[pane].right = m_bar[pane].left + w;
236 } const double hTotal = rc.Height() - (2 * Y_OFFSET); // Height of draw area
238 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
239 nbLines = max(nbLines, m_view[pane]->GetSubLineCount());
241 m_lineInPix = hTotal / nbLines;
242 m_pixInLines = nbLines / hTotal;
243 if (m_lineInPix > MAX_LINEPIX)
245 m_lineInPix = MAX_LINEPIX;
246 m_pixInLines = 1 / MAX_LINEPIX;
249 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
251 m_bar[pane].top = Y_OFFSET - 1;
252 m_bar[pane].bottom = (LONG)(m_lineInPix * nbLines + Y_OFFSET + 1);
257 * @brief Calculate difference lines and coordinates.
258 * This function calculates begin- and end-lines of differences when word-wrap
259 * is enabled. Otherwise the value from original difflist is used. Line
260 * numbers are also converted to coordinates in the window. All calculated
261 * (and not ignored) differences are added to the new list.
263 void CLocationView::CalculateBlocks()
265 // lineposition in pixels.
269 m_diffBlocks.clear();
271 CMergeDoc *pDoc = GetDocument();
272 const int nDiffs = pDoc->m_diffList.GetSize();
274 m_diffBlocks.reserve(nDiffs); // Pre-allocate space for the list.
276 int nLineCount = m_view[0]->GetLineCount();
277 int nDiff = pDoc->m_diffList.FirstSignificantDiff();
281 VERIFY(pDoc->m_diffList.GetDiff(nDiff, diff));
283 CMergeEditView *pView = m_view[0];
288 int minY = INT_MAX, maxY = -1;
290 bs[nBlocks++] = diff.dbegin[0];
291 for (i = 0; i < pDoc->m_nBuffers; i++)
293 if (diff.blank[i] >= 0)
295 if (minY > diff.blank[i])
296 minY = diff.blank[i];
297 if (maxY < diff.blank[i])
298 maxY = diff.blank[i];
303 bs[nBlocks++] = minY;
307 bs[nBlocks++] = minY;
308 bs[nBlocks++] = maxY;
310 bs[nBlocks] = diff.dend[0] + 1;
311 if (bs[nBlocks] >= nLineCount)
312 bs[nBlocks] = nLineCount - 1;
314 for (i = 0; i < nBlocks; i++)
316 CalculateBlocksPixel(
317 pView->GetSubLineIndex(bs[i]),
318 pView->GetSubLineIndex(bs[i + 1]),
319 pView->GetSubLines(bs[i + 1]), nBeginY, nEndY);
321 block.top_line = bs[i];
322 block.bottom_line = bs[i + 1];
323 block.top_coord = nBeginY;
324 block.bottom_coord = nEndY;
325 block.diff_index = nDiff;
327 m_diffBlocks.push_back(block);
330 nDiff = pDoc->m_diffList.NextSignificantDiff(nDiff);
332 m_bRecalculateBlocks = FALSE;
336 * @brief Calculate Blocksize to pixel.
337 * @param [in] nBlockStart line where block starts
338 * @param [in] nBlockEnd line where block ends
339 * @param [in] nBlockLength length of the block
340 * @param [in,out] nBeginY pixel in y where block starts
341 * @param [in,out] nEndY pixel in y where block ends
344 void CLocationView::CalculateBlocksPixel(int nBlockStart, int nBlockEnd,
345 int nBlockLength, int &nBeginY, int &nEndY)
347 // Count how many line does the diff block have.
348 const int nBlockHeight = nBlockEnd - nBlockStart + nBlockLength;
350 // Convert diff block size from lines to pixels.
351 nBeginY = (int)(nBlockStart * m_lineInPix + Y_OFFSET);
352 nEndY = (int)((nBlockStart + nBlockHeight) * m_lineInPix + Y_OFFSET);
355 static COLORREF GetIntermediateColor(COLORREF a, COLORREF b)
357 const int R = (GetRValue(a) - GetRValue(b)) / 2 + GetRValue(b);
358 const int G = (GetGValue(a) - GetGValue(b)) / 2 + GetGValue(b);
359 const int B = (GetBValue(a) - GetBValue(b)) / 2 + GetBValue(b);
363 static COLORREF GetDarkenColor(COLORREF a, double l)
365 const int R = static_cast<int>(GetRValue(a) * l);
366 const int G = static_cast<int>(GetGValue(a) * l);
367 const int B = static_cast<int>(GetBValue(a) * l);
372 * @brief Draw maps of files.
374 * Draws maps of differences in files. Difference list is walked and
375 * every difference is drawn with same colors as in editview.
376 * @note We MUST use doubles when calculating coords to avoid rounding
377 * to integers. Rounding causes miscalculation of coords.
378 * @sa CLocationView::DrawRect()
380 void CLocationView::OnDraw(CDC* pDC)
382 ASSERT(m_view[0] != NULL);
383 ASSERT(m_view[1] != NULL);
385 CMergeDoc *pDoc = GetDocument();
387 if (std::count(m_view, m_view + pDoc->m_nBuffers, static_cast<CMergeEditView *>(NULL)) > 0)
390 if (!m_view[0]->IsInitialized()) return;
392 BOOL bEditedAfterRescan = FALSE;
393 int nPaneNotModified = -1;
394 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
396 if (!pDoc->IsEditedAfterRescan(pane))
397 nPaneNotModified = pane;
399 bEditedAfterRescan = TRUE;
405 CMyMemDC dc(pDC, &rc);
407 COLORREF cr[3] = {CLR_NONE, CLR_NONE, CLR_NONE};
408 COLORREF crt = CLR_NONE; // Text color
411 m_movedLines.RemoveAll();
416 COLORREF clrFace = clrBackground;
417 COLORREF clrShadow = GetSysColor(COLOR_BTNSHADOW);
418 COLORREF clrShadow2 = GetIntermediateColor(clrFace, clrShadow);
419 COLORREF clrShadow3 = GetIntermediateColor(clrFace, clrShadow2);
420 COLORREF clrShadow4 = GetIntermediateColor(clrFace, clrShadow3);
423 CPen* oldObj = (CPen*)dc.SelectStockObject(NULL_PEN);
424 CBrush brush(m_view[0]->GetColor(COLORINDEX_WHITESPACE));
425 CBrush* oldBrush = (CBrush*)dc.SelectObject(&brush);
426 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
428 int nBottom = (int)(m_lineInPix * m_view[pane]->GetSubLineCount() + Y_OFFSET + 1);
429 CBrush *pOldBrush = NULL;
430 if (pDoc->IsEditedAfterRescan(pane))
431 pOldBrush = (CBrush *)dc.SelectStockObject(HOLLOW_BRUSH);
432 dc.Rectangle(m_bar[pane]);
434 dc.SelectObject(pOldBrush);
436 CRect rect = m_bar[pane];
437 rect.InflateRect(1, 1);
438 dc.Draw3dRect(rect, clrShadow4, clrShadow3);
439 rect.InflateRect(-1, -1);
440 dc.Draw3dRect(rect, clrShadow2, clrShadow);
442 dc.SelectObject(oldBrush);
443 dc.SelectObject(oldBrush);
444 dc.SelectObject(oldObj);
446 // Iterate the differences list and draw differences as colored blocks.
448 // Don't recalculate blocks if we earlier determined it is not needed
449 // This may save lots of processing
450 if (m_bRecalculateBlocks)
454 const int nCurDiff = pDoc->GetCurrentDiff();
456 vector<DiffBlock>::const_iterator iter = m_diffBlocks.begin();
457 for (; iter != m_diffBlocks.end(); ++iter)
459 if (nPaneNotModified == -1)
461 CMergeEditView *pView = m_view[nPaneNotModified];
462 const BOOL bInsideDiff = (nCurDiff == (*iter).diff_index);
464 if ((nPrevEndY != (*iter).bottom_coord) || bInsideDiff)
466 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
468 if (pDoc->IsEditedAfterRescan(pane))
470 // Draw 3way-diff state
471 if (pDoc->m_nBuffers == 3 && pane < 2)
473 CRect r(m_bar[pane].right - 1, (*iter).top_coord, m_bar[pane + 1].left + 1, (*iter).bottom_coord);
474 if ((pane == 0 && (*iter).op == OP_3RDONLY) || (pane == 1 && (*iter).op == OP_1STONLY))
475 DrawRect(&dc, r, RGB(255, 255, 127), false);
476 else if ((*iter).op == OP_2NDONLY)
477 DrawRect(&dc, r, RGB(127, 255, 255), false);
478 else if ((*iter).op == OP_DIFF)
479 DrawRect(&dc, r, RGB(255, 0, 0), false);
482 m_view[pane]->GetLineColors2((*iter).top_line, 0, cr[pane], crt, bwh);
483 CRect r(m_bar[pane].left, (*iter).top_coord, m_bar[pane].right, (*iter).bottom_coord);
484 DrawRect(&dc, r, cr[pane], bInsideDiff);
487 nPrevEndY = (*iter).bottom_coord;
489 // Test if we draw a connector
490 BOOL bDisplayConnectorFromLeft = FALSE;
491 BOOL bDisplayConnectorFromRight = FALSE;
493 switch (m_displayMovedBlocks)
495 case DISPLAY_MOVED_FOLLOW_DIFF:
496 // display moved block only for current diff
499 // two sides may be linked to a block somewhere else
500 bDisplayConnectorFromLeft = TRUE;
501 bDisplayConnectorFromRight = TRUE;
503 case DISPLAY_MOVED_ALL:
504 // we display all moved blocks, so once direction is enough
505 bDisplayConnectorFromLeft = TRUE;
511 if (bEditedAfterRescan)
514 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
516 if (bDisplayConnectorFromLeft && pane < 2)
518 int apparent0 = (*iter).top_line;
519 int apparent1 = pDoc->RightLineInMovedBlock(pane, apparent0);
520 const int nBlockHeight = (*iter).bottom_line - (*iter).top_line;
527 apparent0 = pView->GetSubLineIndex(apparent0);
528 apparent1 = pView->GetSubLineIndex(apparent1);
530 start.x = m_bar[pane].right;
531 int leftUpper = (int) (apparent0 * m_lineInPix + Y_OFFSET);
532 int leftLower = (int) ((nBlockHeight + apparent0) * m_lineInPix + Y_OFFSET);
533 start.y = leftUpper + (leftLower - leftUpper) / 2;
534 end.x = m_bar[pane + 1].left;
535 int rightUpper = (int) (apparent1 * m_lineInPix + Y_OFFSET);
536 int rightLower = (int) ((nBlockHeight + apparent1) * m_lineInPix + Y_OFFSET);
537 end.y = rightUpper + (rightLower - rightUpper) / 2;
540 m_movedLines.AddTail(line);
544 if (bDisplayConnectorFromRight && pane > 0)
546 int apparent1 = (*iter).top_line;
547 int apparent0 = pDoc->LeftLineInMovedBlock(pane, apparent1);
548 const int nBlockHeight = (*iter).bottom_line - (*iter).top_line;
555 apparent0 = pView->GetSubLineIndex(apparent0);
556 apparent1 = pView->GetSubLineIndex(apparent1);
558 start.x = m_bar[pane - 1].right;
559 int leftUpper = (int) (apparent0 * m_lineInPix + Y_OFFSET);
560 int leftLower = (int) ((nBlockHeight + apparent0) * m_lineInPix + Y_OFFSET);
561 start.y = leftUpper + (leftLower - leftUpper) / 2;
562 end.x = m_bar[pane].left;
563 int rightUpper = (int) (apparent1 * m_lineInPix + Y_OFFSET);
564 int rightLower = (int) ((nBlockHeight + apparent1) * m_lineInPix + Y_OFFSET);
565 end.y = rightUpper + (rightLower - rightUpper) / 2;
568 m_movedLines.AddTail(line);
574 if (m_displayMovedBlocks != DISPLAY_MOVED_NONE)
575 DrawConnectLines(&dc);
577 m_pSavedBackgroundBitmap.reset(CopyRectToBitmap(&dc, rc));
579 // Since we have invalidated locationbar there is no previous
580 // arearect to remove
582 m_visibleBottom = -1;
583 DrawVisibleAreaRect(&dc);
589 * @brief Draw one block of map.
590 * @param [in] pDC Draw context.
591 * @param [in] r Rectangle to draw.
592 * @param [in] cr Color for rectangle.
593 * @param [in] bSelected Is rectangle for selected difference?
595 void CLocationView::DrawRect(CDC* pDC, const CRect& r, COLORREF cr, BOOL bSelected)
597 // Draw only colored blocks
598 if (cr != CLR_NONE && cr != GetSysColor(COLOR_WINDOW))
602 drawRect.DeflateRect(1, 0);
604 // With long files and small difference areas rect may become 0-height.
605 // Make sure all diffs are drawn at least one pixel height.
606 if (drawRect.Height() < 1)
608 pDC->FillSolidRect(drawRect, cr);
609 CRect drawRect2(drawRect.left, drawRect.top, drawRect.right, drawRect.top + 1);
610 pDC->FillSolidRect(drawRect2, GetDarkenColor(cr, 0.96));
611 CRect drawRect3(drawRect.left, drawRect.bottom - 1, drawRect.right, drawRect.bottom);
612 pDC->FillSolidRect(drawRect3, GetDarkenColor(cr, 0.91));
616 DrawDiffMarker(pDC, r.top);
622 * @brief Capture the mouse target.
624 void CLocationView::OnLButtonDown(UINT nFlags, CPoint point)
628 if (!GotoLocation(point, false))
629 CView::OnLButtonDown(nFlags, point);
633 * @brief Release the mouse target.
635 void CLocationView::OnLButtonUp(UINT nFlags, CPoint point)
639 CView::OnLButtonUp(nFlags, point);
643 * @brief Process drag action on a captured mouse.
645 * Reposition on every dragged movement.
646 * The Screen update stress will be similar to a mouse wheeling.:-)
648 void CLocationView::OnMouseMove(UINT nFlags, CPoint point)
650 if (GetCapture() == this)
652 // Don't go above bars.
653 point.y = max(point.y, Y_OFFSET);
655 // Vertical scroll handlers are range safe, so there is no need to
656 // make sure value is valid and in range.
657 int nSubLine = (int) (m_pixInLines * (point.y - Y_OFFSET));
658 nSubLine -= m_view[0]->GetScreenLines() / 2;
662 // Just a random choose as both view share the same scroll bar.
663 CWnd *pView = m_view[0];
666 si.cbSize = sizeof(si);
669 pView->SetScrollInfo(SB_VERT, &si);
671 // The views are child windows of a splitter windows. Splitter window
672 // doesn't accept scroll bar updates not send from scroll bar control.
673 // So we need to update both views.
675 int nBuffers = GetDocument()->m_nBuffers;
676 for (pane = 0; pane < nBuffers; pane++)
677 m_view[pane]->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 0), NULL);
680 CView::OnMouseMove(nFlags, point);
684 * User left double-clicked mouse
685 * @todo We can give alternative action to a double clicking.
687 void CLocationView::OnLButtonDblClk(UINT nFlags, CPoint point)
689 if (!GotoLocation(point, false))
690 CView::OnLButtonDblClk(nFlags, point);
694 * @brief Scroll both views to point given.
696 * Scroll views to given line. There is two ways to scroll, based on
697 * view lines (ghost lines counted in) or on real lines (no ghost lines).
698 * In most cases view lines should be used as it avoids real line number
699 * calculation and is able to scroll to all lines - real line numbers
700 * cannot be used to scroll to ghost lines.
702 * @param [in] point Point to move to
703 * @param [in] bRealLine TRUE if we want to scroll using real line num,
704 * FALSE if view linenumbers are OK.
705 * @return TRUE if succeeds, FALSE if point not inside bars.
707 bool CLocationView::GotoLocation(const CPoint& point, bool bRealLine)
711 CMergeDoc* pDoc = GetDocument();
713 if (std::count(m_view, m_view + pDoc->m_nBuffers, static_cast<CMergeEditView *>(NULL)) > 0)
717 int bar = IsInsideBar(rc, point);
718 if (bar == BAR_0 || bar == BAR_1 || bar == BAR_2)
720 line = GetLineFromYPos(point.y, bar, bRealLine);
722 else if (bar == BAR_YAREA)
724 // Outside bars, use left bar
726 line = GetLineFromYPos(point.y, bar, FALSE);
731 m_view[0]->GotoLine(line, bRealLine, bar);
732 if (bar == BAR_0 || bar == BAR_1 || bar == BAR_2)
733 m_view[bar]->SetFocus();
739 * @brief Handle scroll events sent directly.
742 void CLocationView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
744 if (pScrollBar == NULL)
746 // Scroll did not come frome a scroll bar
747 // Send it to the right view instead
748 CMergeDoc *pDoc = GetDocument();
749 pDoc->GetView(pDoc->m_nBuffers - 1)->SendMessage(WM_VSCROLL,
750 MAKELONG(nSBCode, nPos), (LPARAM)NULL);
753 CView::OnVScroll (nSBCode, nPos, pScrollBar);
757 * Show context menu and handle user selection.
759 void CLocationView::OnContextMenu(CWnd* pWnd, CPoint point)
761 if (point.x == -1 && point.y == -1)
763 //keystroke invocation
766 ClientToScreen(rect);
768 point = rect.TopLeft();
777 VERIFY(menu.LoadMenu(IDR_POPUP_LOCATIONBAR));
778 theApp.TranslateMenu(menu.m_hMenu);
780 BCMenu* pPopup = (BCMenu *) menu.GetSubMenu(0);
781 ASSERT(pPopup != NULL);
784 cmdUI.m_pMenu = pPopup;
785 cmdUI.m_nIndexMax = cmdUI.m_pMenu->GetMenuItemCount();
786 for (cmdUI.m_nIndex = 0 ; cmdUI.m_nIndex < cmdUI.m_nIndexMax ; ++cmdUI.m_nIndex)
788 cmdUI.m_nID = cmdUI.m_pMenu->GetMenuItemID(cmdUI.m_nIndex);
791 case ID_DISPLAY_MOVED_NONE:
792 cmdUI.SetRadio(m_displayMovedBlocks == DISPLAY_MOVED_NONE);
794 case ID_DISPLAY_MOVED_ALL:
795 cmdUI.SetRadio(m_displayMovedBlocks == DISPLAY_MOVED_ALL);
797 case ID_DISPLAY_MOVED_FOLLOW_DIFF:
798 cmdUI.SetRadio(m_displayMovedBlocks == DISPLAY_MOVED_FOLLOW_DIFF);
806 int bar = IsInsideBar(rc, pt);
808 // If cursor over bar, format string with linenumber, else disable item
811 // If outside bar area use left bar
812 if (bar == BAR_YAREA)
814 nLine = GetLineFromYPos(pt.y, bar);
815 strNum = string_to_str(nLine + 1); // Show linenumber not lineindex
818 pPopup->EnableMenuItem(ID_LOCBAR_GOTODIFF, MF_GRAYED);
819 strItem = string_format_string1(_("G&oto Line %1"), strNum);
820 pPopup->SetMenuText(ID_LOCBAR_GOTODIFF, strItem.c_str(), MF_BYCOMMAND);
822 // invoke context menu
823 // we don't want to use the main application handlers, so we use flags TPM_NONOTIFY | TPM_RETURNCMD
824 // and handle the command after TrackPopupMenu
825 int command = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, AfxGetMainWnd());
827 CMergeDoc* pDoc = GetDocument();
830 case ID_LOCBAR_GOTODIFF:
831 m_view[0]->GotoLine(nLine, true, bar);
832 if (bar == BAR_0 || bar == BAR_1 || bar == BAR_2)
833 m_view[bar]->SetFocus();
838 case ID_DISPLAY_MOVED_NONE:
839 SetConnectMovedBlocks(DISPLAY_MOVED_NONE);
840 pDoc->SetDetectMovedBlocks(FALSE);
842 case ID_DISPLAY_MOVED_ALL:
843 SetConnectMovedBlocks(DISPLAY_MOVED_ALL);
844 pDoc->SetDetectMovedBlocks(TRUE);
846 case ID_DISPLAY_MOVED_FOLLOW_DIFF:
847 SetConnectMovedBlocks(DISPLAY_MOVED_FOLLOW_DIFF);
848 pDoc->SetDetectMovedBlocks(TRUE);
854 * @brief Calculates view/real line in file from given YCoord in bar.
855 * @param [in] nYCoord ycoord in pane
856 * @param [in] bar bar/file
857 * @param [in] bRealLine TRUE if real line is returned, FALSE for view line
858 * @return 0-based index of view/real line in file [0...lines-1]
860 int CLocationView::GetLineFromYPos(int nYCoord, int bar, BOOL bRealLine)
862 CMergeEditView *pView = m_view[bar];
864 int nSubLineIndex = (int) (m_pixInLines * (nYCoord - Y_OFFSET));
866 // Keep sub-line index in range.
867 if (nSubLineIndex < 0)
871 else if (nSubLineIndex >= pView->GetSubLineCount())
873 nSubLineIndex = pView->GetSubLineCount() - 1;
876 // Find the real (not wrapped) line number from sub-line index.
879 pView->GetLineBySubLine(nSubLineIndex, nLine, nSubLine);
881 // Convert line number to line index.
887 // We've got a view line now
888 if (bRealLine == FALSE)
891 // Get real line (exclude ghost lines)
892 CMergeDoc* pDoc = GetDocument();
893 const int nRealLine = pDoc->m_ptBuf[bar]->ComputeRealLine(nLine);
898 * @brief Determines if given coords are inside left/right bar.
899 * @param rc [in] size of locationpane client area
900 * @param pt [in] point we want to check, in client coordinates.
901 * @return LOCBAR_TYPE area where point is.
903 int CLocationView::IsInsideBar(const CRect& rc, const POINT& pt)
905 int retVal = BAR_NONE;
906 CMergeDoc *pDoc = GetDocument();
907 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
909 if (m_bar[pane].PtInRect(pt))
911 retVal = BAR_0 + pane;
919 * @brief Draws rect indicating visible area in file views.
921 * @param [in] nTopLine New topline for indicator
922 * @param [in] nBottomLine New bottomline for indicator
923 * @todo This function dublicates too much DrawRect() code.
925 void CLocationView::DrawVisibleAreaRect(CDC *pClientDC, int nTopLine, int nBottomLine)
927 CMergeDoc* pDoc = GetDocument();
928 const int nScreenLines = pDoc->GetView(0)->GetScreenLines();
931 nTopLine = pDoc->GetView(0)->GetTopSubLine();
933 if (nBottomLine == -1)
935 const int nScreenLines = pDoc->GetView(1)->GetScreenLines();
936 nBottomLine = nTopLine + nScreenLines;
941 int nbLines = INT_MAX;
942 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
943 nbLines = min(nbLines, m_view[pane]->GetSubLineCount());
945 int nTopCoord = static_cast<int>(Y_OFFSET +
946 (static_cast<double>(nTopLine * m_lineInPix)));
947 int nBottomCoord = static_cast<int>(Y_OFFSET +
948 (static_cast<double>(nBottomLine * m_lineInPix)));
950 double xbarBottom = min(nbLines / m_pixInLines + Y_OFFSET, rc.Height() - Y_OFFSET);
951 int barBottom = (int)xbarBottom;
952 // Make sure bottom coord is in bar range
953 nBottomCoord = min(nBottomCoord, barBottom);
955 // Ensure visible area is at least minimum height
956 if (nBottomCoord - nTopCoord < INDICATOR_MIN_HEIGHT)
958 // If area is near top of file, add additional area to bottom
959 // of the bar and vice versa.
960 if (nTopCoord < Y_OFFSET + 20)
961 nBottomCoord += INDICATOR_MIN_HEIGHT - (nBottomCoord - nTopCoord);
964 // Make sure locationbox has min hight
965 if ((nBottomCoord - nTopCoord) < INDICATOR_MIN_HEIGHT)
967 // If we have a high number of lines, it may be better
968 // to keep the topline, otherwise the cursor can
969 // jump up and down unexpected
970 nBottomCoord = nTopCoord + INDICATOR_MIN_HEIGHT;
975 // Store current values for later use (to check if area changes)
976 m_visibleTop = nTopCoord;
977 m_visibleBottom = nBottomCoord;
979 CRect rcVisibleArea(2, m_visibleTop, rc.right - 2, m_visibleBottom);
980 std::unique_ptr<CBitmap> pBitmap(CopyRectToBitmap(pClientDC, rcVisibleArea));
981 std::unique_ptr<CBitmap> pDarkenedBitmap(GetDarkenedBitmap(pClientDC, pBitmap.get()));
982 DrawBitmap(pClientDC, rcVisibleArea.left, rcVisibleArea.top, pDarkenedBitmap.get());
986 * @brief Public function for updating visible area indicator.
988 * @param [in] nTopLine New topline for indicator
989 * @param [in] nBottomLine New bottomline for indicator
991 void CLocationView::UpdateVisiblePos(int nTopLine, int nBottomLine)
995 CMergeDoc *pDoc = GetDocument();
997 IF_IS_TRUE_ALL(m_nSubLineCount[pane] == m_view[pane]->GetSubLineCount(), pane, pDoc->m_nBuffers)
999 int nTopCoord = static_cast<int>(Y_OFFSET +
1000 (static_cast<double>(nTopLine * m_lineInPix)));
1001 int nBottomCoord = static_cast<int>(Y_OFFSET +
1002 (static_cast<double>(nBottomLine * m_lineInPix)));
1003 if (m_visibleTop != nTopCoord || m_visibleBottom != nBottomCoord)
1005 // Visible area was changed
1007 if (m_pSavedBackgroundBitmap)
1010 // Clear previous visible rect
1011 DrawBitmap(&dc, 0, 0, m_pSavedBackgroundBitmap.get());
1013 DrawVisibleAreaRect(&dc, nTopLine, nBottomLine);
1020 InvalidateRect(NULL);
1021 for (pane = 0; pane < pDoc->m_nBuffers; pane++)
1022 m_nSubLineCount[pane] = m_view[pane]->GetSubLineCount();
1028 * @brief Unset pointers to MergeEditView when location pane is closed.
1030 void CLocationView::OnClose()
1032 CMergeDoc* pDoc = GetDocument();
1033 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
1034 m_view[pane]->SetLocationView(NULL);
1040 * @brief Draw lines connecting moved blocks.
1042 void CLocationView::DrawConnectLines(CDC *pClientDC)
1044 CPen* oldObj = (CPen*)pClientDC->SelectStockObject(BLACK_PEN);
1046 POSITION pos = m_movedLines.GetHeadPosition();
1049 MovedLine line = m_movedLines.GetNext(pos);
1050 pClientDC->MoveTo(line.ptLeft.x, line.ptLeft.y);
1051 pClientDC->LineTo(line.ptRight.x, line.ptRight.y);
1054 pClientDC->SelectObject(oldObj);
1058 * @brief Stores HWND of frame window (CChildFrame).
1060 void CLocationView::SetFrameHwnd(HWND hwndFrame)
1062 m_hwndFrame = hwndFrame;
1066 * @brief Request frame window to store sizes.
1068 * When locationview size changes we want to save new size
1069 * for new windows. But we must do it through frame window.
1070 * @param [in] nType Type of resizing, SIZE_MAXIMIZED etc.
1071 * @param [in] cx New panel width.
1072 * @param [in] cy New panel height.
1074 void CLocationView::OnSize(UINT nType, int cx, int cy)
1076 CView::OnSize(nType, cx, cy);
1078 // Height change needs block recalculation
1079 // TODO: Perhaps this should be determined from need to change bar size?
1080 // And we could change bar sizes more lazily, not from every one pixel change in size?
1081 if (cy != m_currentSize.cy)
1082 m_bRecalculateBlocks = TRUE;
1084 if (cx != m_currentSize.cx)
1086 if (m_hwndFrame != NULL)
1087 ::PostMessage(m_hwndFrame, MSG_STORE_PANESIZES, 0, 0);
1090 m_currentSize.cx = cx;
1091 m_currentSize.cy = cy;
1095 * @brief Draw marker for top of currently selected difference.
1096 * This function draws marker for top of currently selected difference.
1097 * This marker makes it a lot easier to see where currently selected
1098 * difference is in location bar. Especially when selected diffence is
1099 * small and it is not easy to find it otherwise.
1100 * @param [in] pDC Pointer to draw context.
1101 * @param [in] yCoord Y-coord of top of difference, -1 if no difference.
1103 void CLocationView::DrawDiffMarker(CDC* pDC, int yCoord)
1105 int nBuffers = GetDocument()->m_nBuffers;
1108 points[0].x = m_bar[0].left - DIFFMARKER_WIDTH - 1;
1109 points[0].y = yCoord - DIFFMARKER_TOP;
1110 points[1].x = m_bar[0].left - 1;
1111 points[1].y = yCoord;
1112 points[2].x = m_bar[0].left - DIFFMARKER_WIDTH - 1;
1113 points[2].y = yCoord + DIFFMARKER_BOTTOM;
1115 COLORREF clrBlue = GetSysColor(COLOR_ACTIVECAPTION);
1116 CPen penDarkBlue(PS_SOLID, 0, GetDarkenColor(clrBlue, 0.9));
1117 CPen* oldObj = (CPen*)pDC->SelectObject(&penDarkBlue);
1118 CBrush brushBlue(clrBlue);
1119 CBrush* pOldBrush = pDC->SelectObject(&brushBlue);
1121 pDC->SetPolyFillMode(WINDING);
1122 pDC->Polygon(points, 3);
1124 points[0].x = m_bar[nBuffers - 1].right + 1 + DIFFMARKER_WIDTH;
1125 points[1].x = m_bar[nBuffers - 1].right + 1;
1126 points[2].x = m_bar[nBuffers - 1].right + 1 + DIFFMARKER_WIDTH;
1127 pDC->Polygon(points, 3);
1129 pDC->SelectObject(pOldBrush);
1130 pDC->SelectObject(oldObj);
1134 * @brief Called when "Save" item is updated
1136 void CLocationView::OnUpdateFileSave(CCmdUI* pCmdUI)
1138 CMergeDoc *pd = GetDocument();
1139 pCmdUI->Enable(pd->m_ptBuf[0]->IsModified() || pd->m_ptBuf[1]->IsModified());
1143 * @brief Called when "Save left (as...)" item is updated
1145 void CLocationView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
1147 CMergeDoc *pd = GetDocument();
1148 pCmdUI->Enable(!pd->m_ptBuf[0]->GetReadOnly() && pd->m_ptBuf[0]->IsModified());
1152 * @brief Called when "Save Middle (as...)" item is updated
1154 void CLocationView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
1156 CMergeDoc *pd = GetDocument();
1157 pCmdUI->Enable(pd->m_nBuffers > 2 && !pd->m_ptBuf[1]->GetReadOnly() && pd->m_ptBuf[1]->IsModified());
1161 * @brief Called when "Save right (as...)" item is updated
1163 void CLocationView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
1165 CMergeDoc *pd = GetDocument();
1166 pCmdUI->Enable(!pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly() && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified());