OSDN Git Service

Fix untranslated strings
[winmerge-jp/winmerge-jp.git] / Src / FilepathEdit.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
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.
10 //
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.
15 //
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.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /** 
22  * @file  FilePathEdit.cpp
23  *
24  * @brief Implementation of the CFilepathEdit class.
25  */
26
27 #include "stdafx.h"
28 #include "FilepathEdit.h"
29 #include "Merge.h"
30 #include "BCMenu.h"
31 #include "ClipBoard.h"
32 #include "Shlwapi.h"
33 #include "paths.h"
34
35 #ifdef _DEBUG
36 #define new DEBUG_NEW
37 #endif
38
39 static int FormatFilePathForDisplayWidth(CDC * pDC, int maxWidth, CString & sFilepath);
40
41 BEGIN_MESSAGE_MAP(CFilepathEdit, CEdit)
42         ON_WM_CONTEXTMENU()
43         ON_WM_CTLCOLOR_REFLECT()
44         ON_WM_NCPAINT()
45 END_MESSAGE_MAP()
46
47
48 /** 
49  * @brief Format the path for display in header control. 
50  *
51  * Formats path so it fits to given length, tries to end lines after
52  * slash characters.
53  *
54  * @param [in] pDC Pointer to draw context.
55  * @param [in] maxWidth Maximum width of the string in the GUI.
56  * @param [in,out] sFilepath:
57  * - in: string to format
58  * - out: formatted string
59  * @return Number of lines path is splitted to.
60  */
61 int FormatFilePathForDisplayWidth(CDC * pDC, int maxWidth, String & sFilepath)
62 {
63         size_t iBegin = 0;
64         int nLines = 1;
65         
66         while (1)
67         {
68                 String line;
69
70                 // find the next truncation point
71                 size_t iEndMin = 0;
72                 size_t iEndMax = sFilepath.length() - iBegin + 1;
73                 while(1)
74                 {
75                         size_t iEnd = (iEndMin + iEndMax) / 2;
76                         if (iEnd == iEndMin)
77                                 break;
78                         line = sFilepath.substr(iBegin, iEnd);
79                         int width = (pDC->GetTextExtent(line.c_str())).cx;
80                         if (width > maxWidth)
81                                 iEndMax = iEnd;
82                         else
83                                 iEndMin = iEnd;
84                 };
85                 ASSERT(iEndMax == iEndMin+1);
86
87                 // here iEndMin is the last character displayed in maxWidth
88
89                 // exit the loop if we can display the remaining characters with no truncation
90                 if (iBegin + iEndMin == sFilepath.length())
91                         break;
92
93                 // truncate the text to the previous "\" if possible
94                 line = sFilepath.substr(iBegin, iEndMin);
95                 size_t lastSlash = line.rfind('\\');
96                 if (lastSlash != String::npos)
97                         iEndMin = lastSlash + 1;
98
99                 sFilepath.insert(iBegin + iEndMin, _T("\n"));
100                 iBegin += iEndMin + 2;
101                 nLines ++;
102         }
103
104         return nLines;
105 }
106
107 /**
108  * @brief Constructor.
109  * Set text color to black and background white by default.
110  */
111 CFilepathEdit::CFilepathEdit()
112  : m_crBackGnd(RGB(255, 255, 255))
113  , m_crText(RGB(0,0,0))
114  , m_bActive(FALSE)
115 {
116 }
117
118 /**
119  * @brief Subclass the control.
120  * @param [in] nID ID of the control to subclass.
121  * @param [in] pParent Parent control of the control to subclass.
122  * @return TRUE if succeeded, FALSE otherwise.
123  */
124 BOOL CFilepathEdit::SubClassEdit(UINT nID, CWnd* pParent)
125 {
126         m_bActive = FALSE;
127         return SubclassDlgItem(nID, pParent);
128 };
129
130 /**
131  * @brief Return the control's original text.
132  * @return Control's original text.
133  */
134 void CFilepathEdit::GetOriginalText(String& rString) const
135 {               
136         rString = m_sOriginalText;
137 }
138
139 /**
140  * @brief Set the text to show in the control.
141  * This function sets the text (original text) to show in the control.
142  * The control may modify the text for displaying in the GUI.
143  */
144 void CFilepathEdit::SetOriginalText(const String& sString)
145 {
146         if (m_sOriginalText.compare(sString) == 0)
147                 return;
148
149         m_sOriginalText = sString;
150
151         RefreshDisplayText();
152 }
153
154 /**
155  * @brief Re-format the displayed text and update GUI.
156  * This method formats the visible text from original text.
157  */
158 void CFilepathEdit::RefreshDisplayText()
159 {
160         String line = m_sOriginalText;
161
162         // we want to keep the first and the last path component, and in between,
163         // as much characters as possible from the right
164         // PathCompactPath keeps, in between, as much characters as possible from the left
165         // so we reverse everything between the first and the last component before calling PathCompactPath
166         size_t iBeginLast = line.rfind('\\');
167         size_t iEndIntro = line.find('\\');
168         if (iBeginLast != String::npos && iEndIntro != iBeginLast)
169         {
170                 String textToReverse = line.substr(iEndIntro + 1, iBeginLast -
171                                 (iEndIntro + 1));
172                 std::reverse(textToReverse.begin(), textToReverse.end());
173                 line = line.substr(0, iEndIntro + 1) + textToReverse + line.substr(iBeginLast);
174         }
175
176         // get a device context object
177         CClientDC lDC(this);
178         // and use the correct font
179         CFont *pFontOld = lDC.SelectObject(GetFont());  
180
181         // compact the path
182         CRect rect;
183         GetRect(rect);
184         // take GetBuffer (lenght +3) to count for ellipsis
185         std::vector<TCHAR> tmp(line.length() + 4);
186         std::copy(line.begin(), line.end(), tmp.begin());
187         PathCompactPath(lDC.GetSafeHdc(), &tmp[0],      rect.Width());
188         line = &tmp[0];
189         
190         // set old font back
191         lDC.SelectObject(pFontOld);
192
193         // we reverse back everything between the first and the last component
194         // it works OK as "..." reversed = "..." again
195         iBeginLast = line.rfind('\\');
196         iEndIntro = line.find('\\');
197         if (iBeginLast != String::npos && iEndIntro != iBeginLast)
198         {
199                 String textToReverse = line.substr(iEndIntro + 1, iBeginLast -
200                                 (iEndIntro+1));
201                 std::reverse(textToReverse.begin(), textToReverse.end());
202                 line = line.substr(0, iEndIntro + 1) + textToReverse + line.substr(iBeginLast);
203         }
204
205         SetWindowText(line.c_str());
206 }
207
208 /**
209  * @brief Updates and returns the tooltip for this edit box
210  */
211 const String& CFilepathEdit::GetUpdatedTipText(CDC * pDC, int maxWidth)
212 {
213         GetOriginalText(m_sToolTipString);
214         FormatFilePathForDisplayWidth(pDC, maxWidth, m_sToolTipString);
215         return m_sToolTipString;
216 }
217
218 /**
219  * @brief retrieve text from the OriginalText
220  *
221  * @note The standard Copy function works with the (compacted) windowText 
222  */
223 void CFilepathEdit::CustomCopy(size_t iBegin, size_t iEnd /*=-1*/)
224 {
225         if (iEnd == String::npos)
226                 iEnd = m_sOriginalText.length();
227
228         PutToClipboard(m_sOriginalText.substr(iBegin, iEnd - iBegin), m_hWnd);
229 }
230
231 /**
232  * @brief Format the context menu.
233  */
234 void CFilepathEdit::OnContextMenu(CWnd*, CPoint point)
235 {
236         {
237                 if (point.x == -1 && point.y == -1){
238                         //keystroke invocation
239                         CRect rect;
240                         GetClientRect(rect);
241                         ClientToScreen(rect);
242
243                         point = rect.TopLeft();
244                         point.Offset(5, 5);
245                 }
246
247                 BCMenu menu;
248                 VERIFY(menu.LoadMenu(IDR_POPUP_EDITOR_HEADERBAR));
249                 theApp.TranslateMenu(menu.m_hMenu);
250
251                 BCMenu* pPopup = static_cast<BCMenu *>(menu.GetSubMenu(0));
252                 ASSERT(pPopup != NULL);
253
254                 DWORD sel = GetSel();
255                 if (HIWORD(sel) == LOWORD(sel))
256                         pPopup->EnableMenuItem(ID_EDITOR_COPY, MF_GRAYED);
257                 if (paths::EndsWithSlash(m_sOriginalText))
258                         // no filename, we have to disable the unwanted menu entry
259                         pPopup->EnableMenuItem(ID_EDITOR_COPY_FILENAME, MF_GRAYED);
260
261                 // invoke context menu
262                 // we don't want to use the main application handlers, so we
263                 // use flags TPM_NONOTIFY | TPM_RETURNCMD
264                 // and handle the command after TrackPopupMenu
265                 int command = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON |
266                         TPM_NONOTIFY  | TPM_RETURNCMD, point.x, point.y, AfxGetMainWnd());
267
268                 // compute the beginning of the text to copy (in OriginalText)
269                 size_t iBegin = 0;
270                 switch (command)
271                 {
272                 case ID_EDITOR_COPY:
273                         Copy();
274                         return;
275                 case ID_EDITOR_COPY_FILENAME:
276                         {
277                         size_t lastSlash = m_sOriginalText.rfind('\\');
278                         if (lastSlash == String::npos)
279                                 lastSlash = m_sOriginalText.rfind('/');
280                         if (lastSlash != String::npos)
281                                 iBegin = lastSlash+1;
282                         else
283                                 iBegin = 0;
284                         }
285                         break;
286                 case ID_EDITOR_COPY_PATH:
287                         // pass the heading "*" for modified files
288                         if (m_sOriginalText.at(0) == '*')
289                                 iBegin = 2;
290                         else
291                                 iBegin = 0;
292                         break;
293                 default:
294                         return;
295                 }
296                 
297                 CustomCopy(iBegin);
298         }
299 }
300
301 static COLORREF GetDarkenColor(COLORREF a, double r)
302 {
303         const int R = static_cast<int>(GetRValue(a) * r);
304         const int G = static_cast<int>(GetGValue(a) * r);
305         const int B = static_cast<int>(GetBValue(a) * r);
306         return RGB(R, G, B);
307 }
308
309 void CFilepathEdit::OnNcPaint()
310 {
311         CWindowDC dc(this);
312         CRect rect;
313         const int margin = 4;
314         GetWindowRect(rect);
315         rect.OffsetRect(-rect.TopLeft());
316         dc.FillSolidRect(CRect(rect.left, rect.top, rect.left + margin, rect.bottom), GetDarkenColor(m_crBackGnd, 0.98));
317         dc.FillSolidRect(CRect(rect.left, rect.top, rect.left + 1, rect.bottom), GetDarkenColor(m_crBackGnd, 0.90));
318         dc.FillSolidRect(CRect(rect.right - margin, rect.top, rect.right, rect.bottom), m_crBackGnd);
319         dc.FillSolidRect(CRect(rect.left + 1, rect.top, rect.right, rect.top + margin), GetDarkenColor(m_crBackGnd, 0.98));
320         dc.FillSolidRect(CRect(rect.left, rect.top, rect.right, rect.top + 1), GetDarkenColor(m_crBackGnd, 0.90));
321         dc.FillSolidRect(CRect(rect.left + margin, rect.bottom - margin, rect.right, rect.bottom), m_crBackGnd);
322 }
323
324 /**
325  * @brief Set the control to look active/inactive.
326  * This function sets control to look like an active control. We don't
327  * have real focus on this control, but editor pane below it. However
328  * for user this active look informs which editor pane is active.
329  * @param [in] bActive If TRUE set control look like active control.
330  */
331 void CFilepathEdit::SetActive(bool bActive)
332 {
333         m_bActive = bActive;
334
335         if (m_hWnd == NULL)
336                 return;
337
338         CRect rcWnd;
339         GetWindowRect(&rcWnd);
340
341         if (bActive)
342         {
343                 SetTextColor(::GetSysColor(COLOR_CAPTIONTEXT));
344                 SetBackColor(::GetSysColor(COLOR_ACTIVECAPTION));
345         }
346         else
347         {
348                 SetTextColor(::GetSysColor(COLOR_INACTIVECAPTIONTEXT));
349                 SetBackColor(::GetSysColor(COLOR_INACTIVECAPTION));
350         }
351         RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
352 }
353
354 /**
355  * @brief Set control's colors.
356  * @param [in] pDC pointer to device context.
357  * @param [in] nCtlColor Control color to set.
358  * @note Parameter @p nCtlColor is not used but must be present as this method
359  * is called by framework.
360  * @return Brush for background.
361  */
362 HBRUSH CFilepathEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
363 {
364         UNUSED_ALWAYS(nCtlColor);
365         // Return a non-NULL brush if the parent's 
366         //handler should not be called
367
368         //set text color
369         pDC->SetTextColor(m_crText);
370
371         //set the text's background color
372         pDC->SetBkColor(m_crBackGnd);
373
374         //return the brush used for background this sets control background
375         return m_brBackGnd;
376 }
377
378 /**
379  * @brief Set control's bacground color.
380  * @param [in] rgb Color to set as background color.
381  */
382 void CFilepathEdit::SetBackColor(COLORREF rgb)
383 {
384         //set background color ref (used for text's background)
385         m_crBackGnd = rgb;
386         
387         //free brush
388         if (m_brBackGnd.GetSafeHandle())
389                 m_brBackGnd.DeleteObject();
390         //set brush to new color
391         m_brBackGnd.CreateSolidBrush(rgb);
392         
393         //redraw
394         Invalidate(TRUE);
395 }
396
397 /**
398  * @brief Set control's text color.
399  * @param [in] Color to set as text color.
400  */
401 void CFilepathEdit::SetTextColor(COLORREF rgb)
402 {
403         //set text color ref
404         m_crText = rgb;
405
406         //redraw
407         Invalidate(TRUE);
408 }