OSDN Git Service

refactor
[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         ON_WM_SETCURSOR()
73 END_MESSAGE_MAP()
74
75 /**
76  * @brief Constructor.
77  */
78 CMergeStatusBar::CMergeStatusBar() : m_nPanes(2), m_bDiff{}, m_dispFlags{}
79 {
80         for (int pane = 0; pane < sizeof(m_status) / sizeof(m_status[0]); pane++)
81         {
82                 m_status[pane].m_pWndStatusBar = this;
83                 m_status[pane].m_base = PANE_PANE0_INFO + pane * nColumnsPerPane;
84         }
85         Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
86 }
87
88 /**
89  * @brief Destructor.
90  */
91 CMergeStatusBar::~CMergeStatusBar()
92 {
93 }
94
95 BOOL CMergeStatusBar::Create(CWnd* pParentWnd)
96 {
97         if (! __super::Create(pParentWnd))
98                 return FALSE;
99
100         SetIndicators(indicatorsBottom, sizeof(indicatorsBottom) / sizeof(UINT));
101
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);
107
108         for (int pane = 0; pane < 3; pane++)
109         {
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);
114         }
115
116         return TRUE;
117 };
118
119 void CMergeStatusBar::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
120 {
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)
124                 return;
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());
129
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);
133
134         if (displayedAll && m_bDiff[pbase] != diff)
135         {
136                 m_bDiff[pbase] = diff;
137                 for (int pane = 0; pane < m_nPanes; ++pane)
138                 {
139                         RECT rcColumn;
140                         GetItemRect(pbase + pane * nColumnsPerPane, &rcColumn);
141                         InvalidateRect(&rcColumn);
142                 }
143                 return;
144         }
145
146         CDC dc;
147         dc.Attach(lpDrawItemStruct->hDC);
148         if (displayedAll && diff)
149         {
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);
154                 dc.ExtTextOut(
155                         lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top,
156                         ETO_OPAQUE, &lpDrawItemStruct->rcItem, _T(""), nullptr );
157         }
158         else
159         {
160                 dc.SetBkMode(TRANSPARENT);
161                 dc.SetTextColor(GetSysColor(COLOR_BTNTEXT));
162         }
163         dc.DrawText(ptext[pcur], &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
164         dc.Detach();
165 }
166
167 void CMergeStatusBar::Resize(int widths[])
168 {
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); };
174
175         for (int pane = 0; pane < m_nPanes; pane++)
176         {
177                 int fixedPaneWidth = pointToPixel(RO_PANEL_WIDTH + ENCODING_PANEL_WIDTH + EOL_PANEL_WIDTH) +
178                         (3 * borderWidth);
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;
183                 if (paneWidth < 0)
184                 {
185                         paneWidth = 0;
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;
194                 }
195
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);
204         }
205 }
206
207 /**
208  * @brief Update any resources necessary after a GUI language change
209  */
210 void CMergeStatusBar::UpdateResources()
211 {
212         for (int pane = 0; pane < m_nPanes; pane++)
213                 m_status[pane].UpdateResources();
214 }
215
216 /// Bridge class which implements the interface from crystal editor to frame status line display
217 CMergeStatusBar::MergeStatus::MergeStatus()
218 : m_nColumn(0)
219 , m_nColumns(0)
220 , m_nChar(0)
221 , m_nChars(0)
222 , m_nCodepage(-1)
223 , m_bHasBom(false)
224 , m_nSelectedLines(0)
225 , m_nSelectedChars(0)
226 , m_pWndStatusBar(nullptr)
227 , m_base(0)
228 {
229 }
230
231 /// Send status line info (about one side of merge view) to screen
232 void CMergeStatusBar::MergeStatus::Update()
233 {
234         if (IsWindow(m_pWndStatusBar->m_hWnd))
235         {
236                 CString strInfo, strEncoding;
237                 if (m_nChars == -1)
238                 {
239                         strInfo.Format(_("Line: %s").c_str(),
240                                 m_sLine.c_str());
241                 }
242                 else if (m_sEolDisplay.empty())
243                 {
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());
246                 }
247                 else
248                 {
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());
251                 }
252                 if (m_nSelectedLines > 0)
253                 {
254                         CString strSelected;
255                         strSelected.Format(_("  Sel: %d | %d").c_str(), m_nSelectedLines, m_nSelectedChars);
256                         strInfo += strSelected;
257                 }
258
259                 if (m_nCodepage > 0)
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);
263         }
264 }
265
266 /**
267  * @brief Update any resources necessary after a GUI language change
268  */
269 void CMergeStatusBar::MergeStatus::UpdateResources()
270 {
271         Update();
272 }
273
274 /// Visible representation of eol
275 static String EolString(const String & sEol)
276 {
277         if (sEol == _T("\r\n"))
278         {
279                 return _("CRLF");
280         }
281         if (sEol == _T("\n"))
282         {
283                 return _("LF");
284         }
285         if (sEol == _T("\r"))
286         {
287                 return _("CR");
288         }
289         if (sEol.empty())
290         {
291                 return _("None");
292         }
293         if (sEol == _T("hidden"))
294                 return _T("");
295         return _T("?");
296 }
297
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)
301 {
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)
306         {
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 }
328
329 BOOL CMergeStatusBar::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
330 {
331     ::SetCursor (::LoadCursor (nullptr, IDC_HAND));
332         return TRUE;
333 }
334