1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
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.
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.
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.
20 /////////////////////////////////////////////////////////////////////////////
22 * @file FilePathEdit.cpp
24 * @brief Implementation of the CFilepathEdit class.
26 // ID line follows -- this is updated by SVN
27 // $Id: FilepathEdit.cpp 6500 2009-02-25 13:36:26Z kimmov $
30 #include "FilepathEdit.h"
33 #include "ClipBoard.h"
41 static char THIS_FILE[] = __FILE__;
44 static int FormatFilePathForDisplayWidth(CDC * pDC, int maxWidth, CString & sFilepath);
46 BEGIN_MESSAGE_MAP(CFilepathEdit, CEdit)
48 ON_WM_CTLCOLOR_REFLECT()
53 * @brief Format the path for display in header control.
55 * Formats path so it fits to given length, tries to end lines after
58 * @param [in] pDC Pointer to draw context.
59 * @param [in] maxWidth Maximum width of the string in the GUI.
60 * @param [in,out] sFilepath:
61 * - in: string to format
62 * - out: formatted string
63 * @return Number of lines path is splitted to.
65 int FormatFilePathForDisplayWidth(CDC * pDC, int maxWidth, String & sFilepath)
74 // find the next truncation point
76 int iEndMax = sFilepath.length() - iBegin + 1;
79 int iEnd = (iEndMin + iEndMax) / 2;
82 line = sFilepath.substr(iBegin, iEnd);
83 int width = (pDC->GetTextExtent(line.c_str())).cx;
89 ASSERT(iEndMax == iEndMin+1);
91 // here iEndMin is the last character displayed in maxWidth
93 // exit the loop if we can display the remaining characters with no truncation
94 if (iBegin + iEndMin == sFilepath.length())
97 // truncate the text to the previous "\" if possible
98 line = sFilepath.substr(iBegin, iEndMin);
99 int lastSlash = line.rfind('\\');
100 if (lastSlash != String::npos)
101 iEndMin = lastSlash + 1;
103 sFilepath.insert(iBegin + iEndMin, _T("\n"));
104 iBegin += iEndMin + 2;
112 * @brief Constructor.
113 * Set text color to black and background white by default.
115 CFilepathEdit::CFilepathEdit()
116 : m_crBackGnd(RGB(255, 255, 255))
117 , m_crText(RGB(0,0,0))
123 * @brief Subclass the control.
124 * @param [in] nID ID of the control to subclass.
125 * @param [in] pParent Parent control of the control to subclass.
126 * @return TRUE if succeeded, FALSE otherwise.
128 BOOL CFilepathEdit::SubClassEdit(UINT nID, CWnd* pParent)
131 return SubclassDlgItem(nID, pParent);
135 * @brief Return the control's original text.
136 * @return Control's original text.
138 void CFilepathEdit::GetOriginalText(String& rString) const
140 rString = m_sOriginalText;
144 * @brief Set the text to show in the control.
145 * This function sets the text (original text) to show in the control.
146 * The control may modify the text for displaying in the GUI.
148 void CFilepathEdit::SetOriginalText(const String& sString)
150 if (m_sOriginalText.compare(sString) == 0)
153 m_sOriginalText = sString;
155 RefreshDisplayText();
159 * @brief Re-format the displayed text and update GUI.
160 * This method formats the visible text from original text.
162 void CFilepathEdit::RefreshDisplayText()
164 String line = m_sOriginalText;
166 // we want to keep the first and the last path component, and in between,
167 // as much characters as possible from the right
168 // PathCompactPath keeps, in between, as much characters as possible from the left
169 // so we reverse everything between the first and the last component before calling PathCompactPath
170 size_t iBeginLast = line.rfind('\\');
171 size_t iEndIntro = line.find('\\');
172 if (iBeginLast != String::npos && iEndIntro != iBeginLast)
174 String textToReverse = line.substr(iEndIntro + 1, iBeginLast -
176 std::reverse(textToReverse.begin(), textToReverse.end());
177 line = line.substr(0, iEndIntro + 1) + textToReverse + line.substr(iBeginLast);
180 // get a device context object
182 // and use the correct font
183 CFont *pFontOld = lDC.SelectObject(GetFont());
188 // take GetBuffer (lenght +3) to count for ellipsis
189 std::vector<TCHAR> tmp(line.length() + 3);
190 std::copy(line.begin(), line.end(), tmp.begin());
191 PathCompactPath(lDC.GetSafeHdc(), &tmp[0], rect.Width());
195 lDC.SelectObject(pFontOld);
197 // we reverse back everything between the first and the last component
198 // it works OK as "..." reversed = "..." again
199 iBeginLast = line.rfind('\\');
200 iEndIntro = line.find('\\');
201 if (iBeginLast != String::npos && iEndIntro != iBeginLast)
203 String textToReverse = line.substr(iEndIntro + 1, iBeginLast -
205 std::reverse(textToReverse.begin(), textToReverse.end());
206 line = line.substr(0, iEndIntro + 1) + textToReverse + line.substr(iBeginLast);
209 SetWindowText(line.c_str());
213 * @brief Updates and returns the tooltip for this edit box
215 const String& CFilepathEdit::GetUpdatedTipText(CDC * pDC, int maxWidth)
217 GetOriginalText(m_sToolTipString);
218 FormatFilePathForDisplayWidth(pDC, maxWidth, m_sToolTipString);
219 return m_sToolTipString;
223 * @brief retrieve text from the OriginalText
225 * @note The standard Copy function works with the (compacted) windowText
227 void CFilepathEdit::CustomCopy(int iBegin, int iEnd /*=-1*/)
230 iEnd = m_sOriginalText.length();
232 PutToClipboard(m_sOriginalText.substr(iBegin, iEnd - iBegin), m_hWnd);
236 * @brief Format the context menu.
238 void CFilepathEdit::OnContextMenu(CWnd*, CPoint point)
241 if (point.x == -1 && point.y == -1){
242 //keystroke invocation
245 ClientToScreen(rect);
247 point = rect.TopLeft();
252 VERIFY(menu.LoadMenu(IDR_POPUP_EDITOR_HEADERBAR));
253 theApp.TranslateMenu(menu.m_hMenu);
255 BCMenu* pPopup = static_cast<BCMenu *>(menu.GetSubMenu(0));
256 ASSERT(pPopup != NULL);
258 DWORD sel = GetSel();
259 if (HIWORD(sel) == LOWORD(sel))
260 pPopup->EnableMenuItem(ID_EDITOR_COPY, MF_GRAYED);
261 if (paths_EndsWithSlash(m_sOriginalText))
262 // no filename, we have to disable the unwanted menu entry
263 pPopup->EnableMenuItem(ID_EDITOR_COPY_FILENAME, MF_GRAYED);
265 // invoke context menu
266 // we don't want to use the main application handlers, so we
267 // use flags TPM_NONOTIFY | TPM_RETURNCMD
268 // and handle the command after TrackPopupMenu
269 int command = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON |
270 TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, AfxGetMainWnd());
272 // compute the beginning of the text to copy (in OriginalText)
279 case ID_EDITOR_COPY_FILENAME:
281 size_t lastSlash = m_sOriginalText.rfind('\\');
282 if (lastSlash == String::npos)
283 lastSlash = m_sOriginalText.rfind('/');
284 if (lastSlash != String::npos)
285 iBegin = lastSlash+1;
290 case ID_EDITOR_COPY_PATH:
291 // pass the heading "*" for modified files
292 if (m_sOriginalText.at(0) == '*')
306 * @brief Set the control to look active/inactive.
307 * This function sets control to look like an active control. We don't
308 * have real focus on this control, but editor pane below it. However
309 * for user this active look informs which editor pane is active.
310 * @param [in] bActive If TRUE set control look like active control.
312 void CFilepathEdit::SetActive(bool bActive)
320 GetWindowRect(&rcWnd);
324 SetTextColor(::GetSysColor(COLOR_CAPTIONTEXT));
325 SetBackColor(::GetSysColor(COLOR_ACTIVECAPTION));
329 SetTextColor(::GetSysColor(COLOR_INACTIVECAPTIONTEXT));
330 SetBackColor(::GetSysColor(COLOR_INACTIVECAPTION));
335 * @brief Set control's colors.
336 * @param [in] pDC pointer to device context.
337 * @param [in] nCtlColor Control color to set.
338 * @note Parameter @p nCtlColor is not used but must be present as this method
339 * is called by framework.
340 * @return Brush for background.
342 HBRUSH CFilepathEdit::CtlColor(CDC* pDC, UINT nCtlColor)
344 UNUSED_ALWAYS(nCtlColor);
345 // Return a non-NULL brush if the parent's
346 //handler should not be called
349 pDC->SetTextColor(m_crText);
351 //set the text's background color
352 pDC->SetBkColor(m_crBackGnd);
354 //return the brush used for background this sets control background
359 * @brief Set control's bacground color.
360 * @param [in] rgb Color to set as background color.
362 void CFilepathEdit::SetBackColor(COLORREF rgb)
364 //set background color ref (used for text's background)
368 if (m_brBackGnd.GetSafeHandle())
369 m_brBackGnd.DeleteObject();
370 //set brush to new color
371 m_brBackGnd.CreateSolidBrush(rgb);
378 * @brief Set control's text color.
379 * @param [in] Color to set as text color.
381 void CFilepathEdit::SetTextColor(COLORREF rgb)