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)
77 CMergeStatusBar::CMergeStatusBar() : m_nPanes(2), m_bDiff{}, m_dispFlags{}
79 for (int pane = 0; pane < sizeof(m_status) / sizeof(m_status[0]); pane++)
81 m_status[pane].m_pWndStatusBar = this;
82 m_status[pane].m_base = PANE_PANE0_INFO + pane * nColumnsPerPane;
84 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
90 CMergeStatusBar::~CMergeStatusBar()
94 BOOL CMergeStatusBar::Create(CWnd* pParentWnd)
96 if (! CStatusBar::Create(pParentWnd))
99 SetIndicators(indicatorsBottom, sizeof(indicatorsBottom) / sizeof(UINT));
101 // Set text to read-only info panes
102 // Text is hidden if file is writable
103 String sText = _("RO");
104 for (auto&& p : { PANE_PANE0_RO, PANE_PANE1_RO, PANE_PANE2_RO })
105 SetPaneText(p, sText.c_str(), TRUE);
107 for (int pane = 0; pane < 3; pane++)
109 SetPaneStyle(PANE_PANE0_INFO + pane * nColumnsPerPane, SBPS_NORMAL);
110 SetPaneStyle(PANE_PANE0_ENCODING + pane * nColumnsPerPane, SBPS_OWNERDRAW);
111 SetPaneStyle(PANE_PANE0_EOL + pane * nColumnsPerPane, SBPS_OWNERDRAW);
112 SetPaneStyle(PANE_PANE0_RO + pane * nColumnsPerPane, SBPS_NORMAL);
118 void CMergeStatusBar::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
120 const int pbase = PANE_PANE0_INFO + (lpDrawItemStruct->itemID - PANE_PANE0_INFO) % nColumnsPerPane;
121 const int pcur = (lpDrawItemStruct->itemID - PANE_PANE0_INFO) / nColumnsPerPane;
122 if (pcur >= m_nPanes)
124 std::vector<CString> ptext(m_nPanes);
125 for (int pane = 0; pane < m_nPanes; ++pane)
126 ptext[pane] = GetPaneText(pbase + pane * nColumnsPerPane);
127 const bool diff = !std::equal(ptext.begin() + 1, ptext.end(), ptext.begin());
129 if (!ptext[pcur].IsEmpty())
130 m_dispFlags[pbase] |= 1 << pcur;
131 const bool displayedAll = m_dispFlags[pbase] == static_cast<unsigned>((1 << m_nPanes) - 1);
133 if (displayedAll && m_bDiff[pbase] != diff)
135 m_bDiff[pbase] = diff;
136 for (int pane = 0; pane < m_nPanes; ++pane)
139 GetItemRect(pbase + pane * nColumnsPerPane, &rcColumn);
140 InvalidateRect(&rcColumn);
146 dc.Attach(lpDrawItemStruct->hDC);
147 if (displayedAll && diff)
149 dc.SetBkMode(OPAQUE);
150 dc.SetTextColor(m_cachedColors.clrWordDiffText == -1 ?
151 theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_NORMALTEXT) : m_cachedColors.clrWordDiffText);
152 dc.SetBkColor(m_cachedColors.clrWordDiff);
154 lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top,
155 ETO_OPAQUE, &lpDrawItemStruct->rcItem, _T(""), nullptr );
159 dc.SetBkMode(TRANSPARENT);
160 dc.SetTextColor(GetSysColor(COLOR_BTNTEXT));
162 dc.DrawText(ptext[pcur], &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
166 void CMergeStatusBar::Resize(int widths[])
168 // Set bottom statusbar panel widths
169 // Kimmo - I don't know why 4 seems to be right for me
170 int borderWidth = 4; // GetSystemMetrics(SM_CXEDGE);
171 const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
172 auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
174 for (int pane = 0; pane < m_nPanes; pane++)
176 int fixedPaneWidth = pointToPixel(RO_PANEL_WIDTH + ENCODING_PANEL_WIDTH + EOL_PANEL_WIDTH) +
178 int paneWidth = widths[pane] - fixedPaneWidth;
179 int encodingWidth = pointToPixel(ENCODING_PANEL_WIDTH) - borderWidth;
180 int roWidth = pointToPixel(RO_PANEL_WIDTH) - borderWidth;
181 int eolWidth = pointToPixel(EOL_PANEL_WIDTH) - borderWidth;
185 int restWidth = widths[pane] - paneWidth - borderWidth;
186 if (restWidth < 0) restWidth = 0;
187 roWidth = (roWidth + borderWidth) * restWidth / fixedPaneWidth - borderWidth;
188 if (roWidth < 0) roWidth = 0;
189 eolWidth = (eolWidth + borderWidth) * restWidth / fixedPaneWidth - borderWidth;
190 if (eolWidth < 0) eolWidth = 0;
191 encodingWidth = widths[pane] - (paneWidth + roWidth + eolWidth + 6 * borderWidth);
192 if (encodingWidth < 0) encodingWidth = 0;
195 SetPaneInfo(PANE_PANE0_INFO + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_INFO + pane,
196 SBPS_NORMAL, paneWidth);
197 SetPaneInfo(PANE_PANE0_ENCODING + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_ENCODING + pane,
198 SBT_OWNERDRAW, encodingWidth);
199 SetPaneInfo(PANE_PANE0_RO + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_RO + pane,
200 SBPS_NORMAL, roWidth);
201 SetPaneInfo(PANE_PANE0_EOL + pane * nColumnsPerPane, ID_STATUS_PANE0FILE_EOL + pane,
202 SBT_OWNERDRAW, eolWidth);
207 * @brief Update any resources necessary after a GUI language change
209 void CMergeStatusBar::UpdateResources()
211 for (int pane = 0; pane < m_nPanes; pane++)
212 m_status[pane].UpdateResources();
215 /// Bridge class which implements the interface from crystal editor to frame status line display
216 CMergeStatusBar::MergeStatus::MergeStatus()
223 , m_nSelectedLines(0)
224 , m_nSelectedChars(0)
225 , m_pWndStatusBar(nullptr)
230 /// Send status line info (about one side of merge view) to screen
231 void CMergeStatusBar::MergeStatus::Update()
233 if (IsWindow(m_pWndStatusBar->m_hWnd))
235 CString strInfo, strEncoding;
238 strInfo.Format(_("Line: %s").c_str(),
241 else if (m_sEolDisplay.empty())
243 strInfo.Format(_("Ln: %s Col: %d/%d Ch: %d/%d").c_str(),
244 m_sLine.c_str(), m_nColumn, m_nColumns, m_nChar, m_nChars, m_nCodepage, m_sCodepageName.c_str());
248 strInfo.Format(_("Ln: %s Col: %d/%d Ch: %d/%d EOL: %s").c_str(),
249 m_sLine.c_str(), m_nColumn, m_nColumns, m_nChar, m_nChars, m_sEolDisplay.c_str(), m_nCodepage, m_sCodepageName.c_str());
251 if (m_nSelectedLines > 0)
254 strSelected.Format(_(" Sel: %d | %d").c_str(), m_nSelectedLines, m_nSelectedChars);
255 strInfo += strSelected;
259 strEncoding.Format(_("%s").c_str(), m_sCodepageName.c_str());
260 m_pWndStatusBar->SetPaneText(m_base, strInfo);
261 m_pWndStatusBar->SetPaneText(m_base + 1, strEncoding);
266 * @brief Update any resources necessary after a GUI language change
268 void CMergeStatusBar::MergeStatus::UpdateResources()
273 /// Visible representation of eol
274 static String EolString(const String & sEol)
276 if (sEol == _T("\r\n"))
280 if (sEol == _T("\n"))
284 if (sEol == _T("\r"))
292 if (sEol == _T("hidden"))
297 /// Receive status line info from crystal window and display
298 void CMergeStatusBar::MergeStatus::SetLineInfo(LPCTSTR szLine, int nColumn,
299 int nColumns, int nChar, int nChars, int nSelectedLines, int nSelectedChars, LPCTSTR szEol, int nCodepage, bool bHasBom)
301 if (m_sLine != szLine || m_nColumn != nColumn || m_nColumns != nColumns ||
302 m_nChar != nChar || m_nChars != nChars ||
303 m_nSelectedLines != nSelectedLines || m_nSelectedChars != nSelectedChars ||
304 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;