1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file MergeStatusBar.cpp
10 * @brief Implementation file for CMergeStatusBar class
14 #include "MergeStatusBar.h"
17 #include "SyntaxColors.h"
24 /** @brief RO status panel width (point) */
25 static const UINT RO_PANEL_WIDTH = 20;
26 /** @brief Encoding status panel width (point) */
27 static const UINT ENCODING_PANEL_WIDTH = 90;
28 /** @brief EOL type status panel width (point) */
29 static const UINT EOL_PANEL_WIDTH = 30;
32 * @brief Statusbar pane indexes
50 const int nColumnsPerPane = PANE_PANE1_INFO - PANE_PANE0_INFO;
53 * @brief Bottom statusbar panels and indicators
55 static UINT indicatorsBottom[] =
71 BEGIN_MESSAGE_MAP(CMergeStatusBar, CStatusBar)
78 CMergeStatusBar::CMergeStatusBar() : m_nPanes(2), m_bDiff{}, m_dispFlags{}
80 for (int pane = 0; pane < sizeof(m_status) / sizeof(m_status[0]); pane++)
82 m_status[pane].m_pWndStatusBar = this;
83 m_status[pane].m_base = PANE_PANE0_INFO + pane * nColumnsPerPane;
85 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
91 CMergeStatusBar::~CMergeStatusBar()
95 BOOL CMergeStatusBar::Create(CWnd* pParentWnd)
97 if (! __super::Create(pParentWnd))
100 SetIndicators(indicatorsBottom, sizeof(indicatorsBottom) / sizeof(UINT));
102 // Set text to read-only info panes
103 // Text is hidden if file is writable
104 String sText = _("RO");
105 for (auto&& p : { PANE_PANE0_RO, PANE_PANE1_RO, PANE_PANE2_RO })
106 SetPaneText(p, sText.c_str(), TRUE);
108 for (int pane = 0; pane < 3; pane++)
110 SetPaneStyle(PANE_PANE0_INFO + pane * nColumnsPerPane, SBPS_NORMAL);
111 SetPaneStyle(PANE_PANE0_ENCODING + pane * nColumnsPerPane, SBPS_OWNERDRAW);
112 SetPaneStyle(PANE_PANE0_EOL + pane * nColumnsPerPane, SBPS_OWNERDRAW);
113 SetPaneStyle(PANE_PANE0_RO + pane * nColumnsPerPane, SBPS_NORMAL);
119 void CMergeStatusBar::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
121 const int pbase = PANE_PANE0_INFO + (lpDrawItemStruct->itemID - PANE_PANE0_INFO) % nColumnsPerPane;
122 const int pcur = (lpDrawItemStruct->itemID - PANE_PANE0_INFO) / nColumnsPerPane;
123 if (pcur >= m_nPanes)
125 std::vector<CString> ptext(m_nPanes);
126 for (int pane = 0; pane < m_nPanes; ++pane)
127 ptext[pane] = GetPaneText(pbase + pane * nColumnsPerPane);
128 const bool diff = !std::equal(ptext.begin() + 1, ptext.end(), ptext.begin());
130 if (!ptext[pcur].IsEmpty())
131 m_dispFlags[pbase] |= 1 << pcur;
132 const bool displayedAll = m_dispFlags[pbase] == static_cast<unsigned>((1 << m_nPanes) - 1);
134 if (displayedAll && m_bDiff[pbase] != diff)
136 m_bDiff[pbase] = diff;
137 for (int pane = 0; pane < m_nPanes; ++pane)
140 GetItemRect(pbase + pane * nColumnsPerPane, &rcColumn);
141 InvalidateRect(&rcColumn);
147 dc.Attach(lpDrawItemStruct->hDC);
148 if (displayedAll && diff)
150 dc.SetBkMode(OPAQUE);
151 dc.SetTextColor(m_cachedColors.clrWordDiffText == -1 ?
152 theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_NORMALTEXT) : m_cachedColors.clrWordDiffText);
153 dc.SetBkColor(m_cachedColors.clrWordDiff);
155 lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top,
156 ETO_OPAQUE, &lpDrawItemStruct->rcItem, _T(""), nullptr );
160 dc.SetBkMode(TRANSPARENT);
161 dc.SetTextColor(GetSysColor(COLOR_BTNTEXT));
163 dc.DrawText(ptext[pcur], &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
167 void CMergeStatusBar::Resize(int widths[])
169 // Set bottom statusbar panel widths
170 // Kimmo - I don't know why 4 seems to be right for me
171 int borderWidth = 4; // GetSystemMetrics(SM_CXEDGE);
172 const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
173 auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
175 for (int pane = 0; pane < m_nPanes; pane++)
177 int fixedPaneWidth = pointToPixel(RO_PANEL_WIDTH + ENCODING_PANEL_WIDTH + EOL_PANEL_WIDTH) +
179 int paneWidth = widths[pane] - fixedPaneWidth;
180 int encodingWidth = pointToPixel(ENCODING_PANEL_WIDTH) - borderWidth;
181 int roWidth = pointToPixel(RO_PANEL_WIDTH) - borderWidth;
182 int eolWidth = pointToPixel(EOL_PANEL_WIDTH) - borderWidth;
186 int restWidth = widths[pane] - paneWidth - borderWidth;
187 if (restWidth < 0) restWidth = 0;
188 roWidth = (roWidth + borderWidth) * restWidth / fixedPaneWidth - borderWidth;
189 if (roWidth < 0) roWidth = 0;
190 eolWidth = (eolWidth + borderWidth) * restWidth / fixedPaneWidth - borderWidth;
191 if (eolWidth < 0) eolWidth = 0;
192 encodingWidth = widths[pane] - (paneWidth + roWidth + eolWidth + 6 * borderWidth);
193 if (encodingWidth < 0) encodingWidth = 0;
196 SetPaneInfo(PANE_PANE0_INFO + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_INFO + pane,
197 SBPS_NORMAL, paneWidth);
198 SetPaneInfo(PANE_PANE0_ENCODING + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_ENCODING + pane,
199 SBT_OWNERDRAW, encodingWidth);
200 SetPaneInfo(PANE_PANE0_RO + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_RO + pane,
201 SBPS_NORMAL, roWidth);
202 SetPaneInfo(PANE_PANE0_EOL + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_EOL + pane,
203 SBT_OWNERDRAW, eolWidth);
208 * @brief Update any resources necessary after a GUI language change
210 void CMergeStatusBar::UpdateResources()
212 for (int pane = 0; pane < m_nPanes; pane++)
213 m_status[pane].UpdateResources();
216 /// Bridge class which implements the interface from crystal editor to frame status line display
217 CMergeStatusBar::MergeStatus::MergeStatus()
224 , m_nSelectedLines(0)
225 , m_nSelectedChars(0)
226 , m_pWndStatusBar(nullptr)
231 /// Send status line info (about one side of merge view) to screen
232 void CMergeStatusBar::MergeStatus::Update()
234 if (IsWindow(m_pWndStatusBar->m_hWnd))
236 CString strInfo, strEncoding;
239 strInfo.Format(_("Line: %s").c_str(),
242 else if (m_sEolDisplay.empty())
244 strInfo.Format(_("Ln: %s Col: %d/%d Ch: %d/%d").c_str(),
245 m_sLine.c_str(), m_nColumn, m_nColumns, m_nChar, m_nChars, m_nCodepage, m_sCodepageName.c_str());
249 strInfo.Format(_("Ln: %s Col: %d/%d Ch: %d/%d EOL: %s").c_str(),
250 m_sLine.c_str(), m_nColumn, m_nColumns, m_nChar, m_nChars, m_sEolDisplay.c_str(), m_nCodepage, m_sCodepageName.c_str());
252 if (m_nSelectedLines > 0)
255 strSelected.Format(_(" Sel: %d | %d").c_str(), m_nSelectedLines, m_nSelectedChars);
256 strInfo += strSelected;
260 strEncoding.Format(_("%s").c_str(), m_sCodepageName.c_str());
261 m_pWndStatusBar->SetPaneText(m_base, strInfo);
262 m_pWndStatusBar->SetPaneText(m_base + 1, strEncoding);
267 * @brief Update any resources necessary after a GUI language change
269 void CMergeStatusBar::MergeStatus::UpdateResources()
274 /// Visible representation of eol
275 static String EolString(const String & sEol)
277 if (sEol == _T("\r\n"))
281 if (sEol == _T("\n"))
285 if (sEol == _T("\r"))
293 if (sEol == _T("hidden"))
298 /// Receive status line info from crystal window and display
299 void CMergeStatusBar::MergeStatus::SetLineInfo(const tchar_t* szLine, int nColumn,
300 int nColumns, int nChar, int nChars, int nSelectedLines, int nSelectedChars, const tchar_t* szEol, int nCodepage, bool bHasBom)
302 if (m_sLine != szLine || m_nColumn != nColumn || m_nColumns != nColumns ||
303 m_nChar != nChar || m_nChars != nChars ||
304 m_nSelectedLines != nSelectedLines || m_nSelectedChars != nSelectedChars ||
305 m_sEol != szEol != 0 || m_nCodepage != nCodepage || m_bHasBom != bHasBom)
309 m_nColumns = nColumns;
312 m_nSelectedLines = nSelectedLines;
313 m_nSelectedChars = nSelectedChars;
315 m_sEolDisplay = EolString(m_sEol);
316 if (m_nCodepage != nCodepage || m_bHasBom != bHasBom)
318 const char *pszCodepageName = GetEncodingNameFromCodePage(nCodepage);
319 m_sCodepageName = pszCodepageName ? ucr::toTString(pszCodepageName) : _T("");
321 m_sCodepageName += _T(" BOM");
323 m_nCodepage = nCodepage;
329 BOOL CMergeStatusBar::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
331 ::SetCursor (::LoadCursor (nullptr, IDC_HAND));