1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "HistoryCombo.h"
\r
21 #include "../registry.h"
\r
23 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
\r
24 #include "../SysImageList.h"
\r
27 #define MAX_HISTORY_ITEMS 25
\r
29 CHistoryCombo::CHistoryCombo(BOOL bAllowSortStyle /*=FALSE*/ )
\r
31 m_nMaxHistoryItems = MAX_HISTORY_ITEMS;
\r
32 m_bAllowSortStyle = bAllowSortStyle;
\r
33 m_bURLHistory = FALSE;
\r
34 m_bPathHistory = FALSE;
\r
35 m_hWndToolTip = NULL;
\r
40 CHistoryCombo::~CHistoryCombo()
\r
44 BOOL CHistoryCombo::PreCreateWindow(CREATESTRUCT& cs)
\r
46 if (!m_bAllowSortStyle) //turn off CBS_SORT style
\r
47 cs.style &= ~CBS_SORT;
\r
48 cs.style |= CBS_AUTOHSCROLL;
\r
50 return CComboBoxEx::PreCreateWindow(cs);
\r
53 BOOL CHistoryCombo::PreTranslateMessage(MSG* pMsg)
\r
56 if (pMsg->message == WM_KEYDOWN)
\r
58 bool bShift = !!(GetKeyState(VK_SHIFT) & 0x8000);
\r
59 int nVirtKey = (int) pMsg->wParam;
\r
61 if (nVirtKey == VK_RETURN)
\r
62 return OnReturnKeyPressed();
\r
63 else if (nVirtKey == VK_DELETE && bShift && GetDroppedState() )
\r
65 RemoveSelectedItem();
\r
69 if (pMsg->message == WM_MOUSEMOVE && this->m_bDyn )
\r
71 if ((pMsg->wParam & MK_LBUTTON) == 0)
\r
74 pt.x = LOWORD(pMsg->lParam);
\r
75 pt.y = HIWORD(pMsg->lParam);
\r
76 OnMouseMove(pMsg->wParam, pt);
\r
81 return CComboBoxEx::PreTranslateMessage(pMsg);
\r
84 BEGIN_MESSAGE_MAP(CHistoryCombo, CComboBoxEx)
\r
90 int CHistoryCombo::AddString(CString str, INT_PTR pos)
\r
95 COMBOBOXEXITEM cbei;
\r
96 SecureZeroMemory(&cbei, sizeof cbei);
\r
97 cbei.mask = CBEIF_TEXT;
\r
100 cbei.iItem = GetCount();
\r
105 CString combostring = str;
\r
106 combostring.Replace('\r', ' ');
\r
107 combostring.Replace('\n', ' ');
\r
108 cbei.pszText = const_cast<LPTSTR>(combostring.GetString());
\r
110 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
\r
113 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
\r
114 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
\r
116 if (str.Left(5) == _T("http:"))
\r
117 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".html"));
\r
118 else if (str.Left(6) == _T("https:"))
\r
119 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".shtml"));
\r
120 else if (str.Left(5) == _T("file:"))
\r
121 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
\r
122 else if (str.Left(4) == _T("git:"))
\r
123 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
\r
124 else if (str.Left(4) == _T("ssh:"))
\r
125 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
\r
127 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
\r
129 cbei.iSelectedImage = cbei.iImage;
\r
130 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
\r
132 if (m_bPathHistory)
\r
134 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
\r
135 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
\r
137 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
\r
139 cbei.iSelectedImage = cbei.iImage;
\r
140 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
\r
143 int nRet = InsertItem(&cbei);
\r
145 m_arEntries.InsertAt(nRet, str);
\r
147 //search the Combo for another string like this
\r
148 //and delete it if one is found
\r
150 int nIndex = FindStringExact(0, combostring);
\r
151 if (nIndex != -1 && nIndex != nRet)
\r
153 DeleteItem(nIndex);
\r
154 m_arEntries.RemoveAt(nIndex);
\r
157 //truncate list to m_nMaxHistoryItems
\r
158 int nNumItems = GetCount();
\r
159 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
\r
161 DeleteItem(m_nMaxHistoryItems);
\r
162 m_arEntries.RemoveAt(m_nMaxHistoryItems);
\r
169 CString CHistoryCombo::LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix)
\r
171 if (lpszSection == NULL || lpszKeyPrefix == NULL || *lpszSection == '\0')
\r
174 m_sSection = lpszSection;
\r
175 m_sKeyPrefix = lpszKeyPrefix;
\r
181 //keys are of form <lpszKeyPrefix><entrynumber>
\r
183 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n++);
\r
184 sText = CRegString(sKey);
\r
185 if (!sText.IsEmpty())
\r
187 } while (!sText.IsEmpty() && n < m_nMaxHistoryItems);
\r
191 ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 0);
\r
193 // need to resize the control for correct display
\r
195 GetWindowRect(rect);
\r
196 GetParent()->ScreenToClient(rect);
\r
197 MoveWindow(rect.left, rect.top, rect.Width(),100);
\r
202 void CHistoryCombo::SaveHistory()
\r
204 if (m_sSection.IsEmpty())
\r
207 //add the current item to the history
\r
209 GetWindowText(sCurItem);
\r
211 if (!sCurItem.IsEmpty())
\r
212 AddString(sCurItem, 0);
\r
213 //save history to registry/inifile
\r
214 int nMax = min(GetCount(), m_nMaxHistoryItems + 1);
\r
215 for (int n = 0; n < nMax; n++)
\r
218 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
\r
219 CRegString regkey = CRegString(sKey);
\r
220 regkey = m_arEntries.GetAt(n);
\r
222 //remove items exceeding the max number of history items
\r
223 for (int n = nMax; ; n++)
\r
226 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
\r
227 CRegString regkey = CRegString(sKey);
\r
228 CString sText = regkey;
\r
229 if (sText.IsEmpty())
\r
231 regkey.removeValue(); // remove entry
\r
235 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries/*=TRUE*/)
\r
238 if (! m_sSection.IsEmpty() && bDeleteRegistryEntries)
\r
240 //remove profile entries
\r
242 for (int n = 0; ; n++)
\r
244 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
\r
245 CRegString regkey = CRegString(sKey);
\r
246 CString sText = regkey;
\r
247 if (sText.IsEmpty())
\r
249 regkey.removeValue(); // remove entry
\r
254 void CHistoryCombo::SetURLHistory(BOOL bURLHistory)
\r
256 m_bURLHistory = bURLHistory;
\r
262 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
\r
263 if (NULL == hwndEdit)
\r
265 // Try the unofficial way of getting the edit control CWnd*
\r
266 CWnd* pWnd = this->GetDlgItem(1001);
\r
269 hwndEdit = pWnd->GetSafeHwnd();
\r
273 SHAutoComplete(hwndEdit, SHACF_URLALL);
\r
276 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
\r
277 SetImageList(&SYS_IMAGE_LIST());
\r
281 void CHistoryCombo::SetPathHistory(BOOL bPathHistory)
\r
283 m_bPathHistory = bPathHistory;
\r
285 if (m_bPathHistory)
\r
289 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
\r
290 if (NULL == hwndEdit)
\r
292 //if not, try the old standby
\r
295 CWnd* pWnd = this->GetDlgItem(1001);
\r
298 hwndEdit = pWnd->GetSafeHwnd();
\r
303 SHAutoComplete(hwndEdit, SHACF_FILESYSTEM);
\r
306 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
\r
307 SetImageList(&SYS_IMAGE_LIST());
\r
311 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems)
\r
313 m_nMaxHistoryItems = nMaxItems;
\r
315 //truncate list to nMaxItems
\r
316 int nNumItems = GetCount();
\r
317 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
\r
318 DeleteString(m_nMaxHistoryItems);
\r
320 void CHistoryCombo::AddString(STRING_VECTOR &list)
\r
322 for(unsigned int i=0;i<list.size();i++)
\r
324 AddString(list[i]);
\r
327 CString CHistoryCombo::GetString() const
\r
332 DWORD style=GetStyle();
\r
336 GetWindowText(str);
\r
340 if ((m_bURLHistory)||(m_bPathHistory) || (!(style&CBS_SIMPLE)) )
\r
342 //URL and path history combo boxes are editable, so get
\r
343 //the string directly from the combobox
\r
344 GetLBText(sel, str.GetBuffer(GetLBTextLen(sel)));
\r
345 str.ReleaseBuffer();
\r
348 return m_arEntries.GetAt(sel);
\r
351 BOOL CHistoryCombo::RemoveSelectedItem()
\r
353 int nIndex = GetCurSel();
\r
354 if (nIndex == CB_ERR)
\r
359 DeleteItem(nIndex);
\r
360 m_arEntries.RemoveAt(nIndex);
\r
362 if ( nIndex < GetCount() )
\r
364 // index stays the same to select the
\r
365 // next item after the item which has
\r
366 // just been deleted
\r
370 // the end of the list has been reached
\r
371 // so we select the previous item
\r
375 if ( nIndex == -1 )
\r
377 // The one and only item has just been
\r
378 // deleted -> reset window text since
\r
379 // there is no item to select
\r
380 SetWindowText(_T(""));
\r
387 // Since the dialog might be canceled we
\r
388 // should now save the history. Before that
\r
389 // set the selection to the first item so that
\r
390 // the items will not be reordered and restore
\r
391 // the selection after saving.
\r
394 if ( nIndex != -1 )
\r
402 void CHistoryCombo::PreSubclassWindow()
\r
404 CComboBoxEx::PreSubclassWindow();
\r
410 void CHistoryCombo::OnMouseMove(UINT nFlags, CPoint point)
\r
413 GetClientRect(&rectClient);
\r
414 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
\r
415 rectClient.right = rectClient.right - nComboButtonWidth;
\r
417 if (rectClient.PtInRect(point))
\r
419 ClientToScreen(&rectClient);
\r
421 CString strText = GetString();
\r
422 m_ToolInfo.lpszText = (LPTSTR)(LPCTSTR)strText;
\r
424 HDC hDC = ::GetDC(m_hWnd);
\r
426 CFont *pFont = GetFont();
\r
427 HFONT hOldFont = (HFONT) ::SelectObject(hDC, (HFONT) *pFont);
\r
430 ::GetTextExtentPoint32(hDC, strText, strText.GetLength(), &size);
\r
431 ::SelectObject(hDC, hOldFont);
\r
432 ::ReleaseDC(m_hWnd, hDC);
\r
434 if (size.cx > (rectClient.Width() - 6))
\r
436 rectClient.left += 1;
\r
437 rectClient.top += 3;
\r
439 COLORREF rgbText = ::GetSysColor(COLOR_WINDOWTEXT);
\r
440 COLORREF rgbBackground = ::GetSysColor(COLOR_WINDOW);
\r
442 CWnd *pWnd = GetFocus();
\r
445 if (pWnd->m_hWnd == m_hWnd)
\r
447 rgbText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
\r
448 rgbBackground = ::GetSysColor(COLOR_HIGHLIGHT);
\r
454 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, rgbBackground, 0);
\r
455 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, rgbText, 0);
\r
456 ::SendMessage(m_hWndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM) &m_ToolInfo);
\r
457 ::SendMessage(m_hWndToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rectClient.left, rectClient.top));
\r
458 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
\r
459 SetTimer(1, 80, NULL);
\r
460 SetTimer(2, 2000, NULL);
\r
466 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
\r
472 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
\r
476 CComboBoxEx::OnMouseMove(nFlags, point);
\r
479 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent)
\r
482 DWORD ptW = GetMessagePos();
\r
483 point.x = GET_X_LPARAM(ptW);
\r
484 point.y = GET_Y_LPARAM(ptW);
\r
485 ScreenToClient(&point);
\r
488 GetClientRect(&rectClient);
\r
489 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
\r
491 rectClient.right = rectClient.right - nComboButtonWidth;
\r
493 if (!rectClient.PtInRect(point))
\r
495 KillTimer(nIDEvent);
\r
496 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
\r
501 // tooltip timeout, just deactivate it
\r
502 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
\r
503 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again
\r
504 // without the mouse pointer first leaving the control and entering it again
\r
507 CComboBoxEx::OnTimer(nIDEvent);
\r
510 void CHistoryCombo::CreateToolTip()
\r
513 m_hWndToolTip = ::CreateWindowEx(WS_EX_TOPMOST,
\r
516 TTS_NOPREFIX | TTS_ALWAYSTIP,
\r
526 // initialize tool info struct
\r
527 memset(&m_ToolInfo, 0, sizeof(m_ToolInfo));
\r
528 m_ToolInfo.cbSize = sizeof(m_ToolInfo);
\r
529 m_ToolInfo.uFlags = TTF_TRANSPARENT;
\r
530 m_ToolInfo.hwnd = m_hWnd;
\r
532 ::SendMessage(m_hWndToolTip, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
\r
533 ::SendMessage(m_hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);
\r
534 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, ::GetSysColor(COLOR_HIGHLIGHT), 0);
\r
535 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, ::GetSysColor(COLOR_HIGHLIGHTTEXT), 0);
\r
537 CRect rectMargins(0,-1,0,-1);
\r
538 ::SendMessage(m_hWndToolTip, TTM_SETMARGIN, 0, (LPARAM)&rectMargins);
\r
540 CFont *pFont = GetFont();
\r
541 ::SendMessage(m_hWndToolTip, WM_SETFONT, (WPARAM)(HFONT)*pFont, FALSE);
\r
544 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct)
\r
546 if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)
\r