OSDN Git Service

Update Dutch.po (#842)
[winmerge-jp/winmerge-jp.git] / Src / MergeStatusBar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //    SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
7 /** 
8  * @file  MergeStatusBar.cpp
9  *
10  * @brief Implementation file for CMergeStatusBar class
11  */
12
13 #include "stdafx.h"
14 #include "MergeStatusBar.h"
15 #include "charsets.h"
16 #include "unicoder.h"
17 #include "SyntaxColors.h"
18 #include "Merge.h"
19
20 #ifdef _DEBUG
21 #define new DEBUG_NEW
22 #endif
23
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;
30
31 /**
32  * @brief Statusbar pane indexes
33  */
34 enum
35 {
36         PANE_PANE0_INFO = 0,
37         PANE_PANE0_ENCODING,
38         PANE_PANE0_EOL,
39         PANE_PANE0_RO,
40         PANE_PANE1_INFO,
41         PANE_PANE1_ENCODING,
42         PANE_PANE1_EOL,
43         PANE_PANE1_RO,
44         PANE_PANE2_INFO,
45         PANE_PANE2_ENCODING,
46         PANE_PANE2_EOL,
47         PANE_PANE2_RO,
48 };
49
50 const int nColumnsPerPane = PANE_PANE1_INFO - PANE_PANE0_INFO;
51
52 /**
53  * @brief Bottom statusbar panels and indicators
54  */
55 static UINT indicatorsBottom[] =
56 {
57         ID_SEPARATOR,
58         ID_SEPARATOR,
59         ID_SEPARATOR,
60         ID_SEPARATOR,
61         ID_SEPARATOR,
62         ID_SEPARATOR,
63         ID_SEPARATOR,
64         ID_SEPARATOR,
65         ID_SEPARATOR,
66         ID_SEPARATOR,
67         ID_SEPARATOR,
68         ID_SEPARATOR,
69 };
70
71 BEGIN_MESSAGE_MAP(CMergeStatusBar, CStatusBar)
72 END_MESSAGE_MAP()
73
74 /**
75  * @brief Constructor.
76  */
77 CMergeStatusBar::CMergeStatusBar() : m_nPanes(2), m_bDiff{}, m_dispFlags{}
78 {
79         for (int pane = 0; pane < sizeof(m_status) / sizeof(m_status[0]); pane++)
80         {
81                 m_status[pane].m_pWndStatusBar = this;
82                 m_status[pane].m_base = PANE_PANE0_INFO + pane * nColumnsPerPane;
83         }
84         Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
85 }
86
87 /**
88  * @brief Destructor.
89  */
90 CMergeStatusBar::~CMergeStatusBar()
91 {
92 }
93
94 BOOL CMergeStatusBar::Create(CWnd* pParentWnd)
95 {
96         if (! CStatusBar::Create(pParentWnd))
97                 return FALSE;
98
99         SetIndicators(indicatorsBottom, sizeof(indicatorsBottom) / sizeof(UINT));
100
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);
106
107         for (int pane = 0; pane < 3; pane++)
108         {
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);
113         }
114
115         return TRUE;
116 };
117
118 void CMergeStatusBar::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
119 {
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)
123                 return;
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());
128
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);
132
133         if (displayedAll && m_bDiff[pbase] != diff)
134         {
135                 m_bDiff[pbase] = diff;
136                 for (int pane = 0; pane < m_nPanes; ++pane)
137                 {
138                         RECT rcColumn;
139                         GetItemRect(pbase + pane * nColumnsPerPane, &rcColumn);
140                         InvalidateRect(&rcColumn);
141                 }
142                 return;
143         }
144
145         CDC dc;
146         dc.Attach(lpDrawItemStruct->hDC);
147         if (displayedAll && diff)
148         {
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);
153                 dc.ExtTextOut(
154                         lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top,
155                         ETO_OPAQUE, &lpDrawItemStruct->rcItem, _T(""), nullptr );
156         }
157         else
158         {
159                 dc.SetBkMode(TRANSPARENT);
160                 dc.SetTextColor(GetSysColor(COLOR_BTNTEXT));
161         }
162         dc.DrawText(ptext[pcur], &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
163         dc.Detach();
164 }
165
166 void CMergeStatusBar::Resize(int widths[])
167 {
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); };
173
174         for (int pane = 0; pane < m_nPanes; pane++)
175         {
176                 int fixedPaneWidth = pointToPixel(RO_PANEL_WIDTH + ENCODING_PANEL_WIDTH + EOL_PANEL_WIDTH) +
177                         (3 * borderWidth);
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;
182                 if (paneWidth < 0)
183                 {
184                         paneWidth = 0;
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;
193                 }
194
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);
203         }
204 }
205
206 /**
207  * @brief Update any resources necessary after a GUI language change
208  */
209 void CMergeStatusBar::UpdateResources()
210 {
211         for (int pane = 0; pane < m_nPanes; pane++)
212                 m_status[pane].UpdateResources();
213 }
214
215 /// Bridge class which implements the interface from crystal editor to frame status line display
216 CMergeStatusBar::MergeStatus::MergeStatus()
217 : m_nColumn(0)
218 , m_nColumns(0)
219 , m_nChar(0)
220 , m_nChars(0)
221 , m_nCodepage(-1)
222 , m_bHasBom(false)
223 , m_nSelectedLines(0)
224 , m_nSelectedChars(0)
225 , m_pWndStatusBar(nullptr)
226 , m_base(0)
227 {
228 }
229
230 /// Send status line info (about one side of merge view) to screen
231 void CMergeStatusBar::MergeStatus::Update()
232 {
233         if (IsWindow(m_pWndStatusBar->m_hWnd))
234         {
235                 CString strInfo, strEncoding;
236                 if (m_nChars == -1)
237                 {
238                         strInfo.Format(_("Line: %s").c_str(),
239                                 m_sLine.c_str());
240                 }
241                 else if (m_sEolDisplay.empty())
242                 {
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());
245                 }
246                 else
247                 {
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());
250                 }
251                 if (m_nSelectedLines > 0)
252                 {
253                         CString strSelected;
254                         strSelected.Format(_("  Sel: %d | %d").c_str(), m_nSelectedLines, m_nSelectedChars);
255                         strInfo += strSelected;
256                 }
257
258                 if (m_nCodepage > 0)
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);
262         }
263 }
264
265 /**
266  * @brief Update any resources necessary after a GUI language change
267  */
268 void CMergeStatusBar::MergeStatus::UpdateResources()
269 {
270         Update();
271 }
272
273 /// Visible representation of eol
274 static String EolString(const String & sEol)
275 {
276         if (sEol == _T("\r\n"))
277         {
278                 return _("CRLF");
279         }
280         if (sEol == _T("\n"))
281         {
282                 return _("LF");
283         }
284         if (sEol == _T("\r"))
285         {
286                 return _("CR");
287         }
288         if (sEol.empty())
289         {
290                 return _("None");
291         }
292         if (sEol == _T("hidden"))
293                 return _T("");
294         return _T("?");
295 }
296
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)
300 {
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)
305         {
306                 USES_CONVERSION;
307                 m_sLine = szLine;
308                 m_nColumn = nColumn;
309                 m_nColumns = nColumns;
310                 m_nChar = nChar;
311                 m_nChars = nChars;
312                 m_nSelectedLines = nSelectedLines;
313                 m_nSelectedChars = nSelectedChars;
314                 m_sEol = szEol;
315                 m_sEolDisplay = EolString(m_sEol);
316                 if (m_nCodepage != nCodepage || m_bHasBom != bHasBom)
317                 {
318                         const char *pszCodepageName = GetEncodingNameFromCodePage(nCodepage);
319                         m_sCodepageName = pszCodepageName ? ucr::toTString(pszCodepageName) : _T("");
320                         if (bHasBom)
321                                 m_sCodepageName += _T(" BOM");
322                 }
323                 m_nCodepage = nCodepage;
324                 m_bHasBom = bHasBom;
325                 Update();
326         }
327 }