Cleanup
[TortoiseGit.git] / src / Utils / MiscUI / HistoryCombo.cpp
blobf304a9ee14d86f923858a9ab6b10f7a03f65055c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "HistoryCombo.h"
21 #include "../registry.h"
23 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
24 #include "../SysImageList.h"
25 #endif
27 #define MAX_HISTORY_ITEMS 25
29 CHistoryCombo::CHistoryCombo(BOOL bAllowSortStyle /*=FALSE*/ )
31 m_nMaxHistoryItems = MAX_HISTORY_ITEMS;
32 m_bAllowSortStyle = bAllowSortStyle;
33 m_bURLHistory = FALSE;
34 m_bPathHistory = FALSE;
35 m_hWndToolTip = NULL;
36 m_ttShown = FALSE;
37 m_bDyn = FALSE;
38 m_bWantReturn = FALSE;
41 CHistoryCombo::~CHistoryCombo()
45 BOOL CHistoryCombo::PreCreateWindow(CREATESTRUCT& cs)
47 if (!m_bAllowSortStyle) //turn off CBS_SORT style
48 cs.style &= ~CBS_SORT;
49 cs.style |= CBS_AUTOHSCROLL;
50 m_bDyn = TRUE;
51 return CComboBoxEx::PreCreateWindow(cs);
54 BOOL CHistoryCombo::PreTranslateMessage(MSG* pMsg)
56 if (pMsg->message == WM_KEYDOWN)
58 bool bShift = !!(GetKeyState(VK_SHIFT) & 0x8000);
59 int nVirtKey = (int) pMsg->wParam;
61 if (nVirtKey == VK_RETURN)
62 return OnReturnKeyPressed();
63 else if (nVirtKey == VK_DELETE && bShift && GetDroppedState() )
65 RemoveSelectedItem();
66 return TRUE;
69 if (nVirtKey == 'A' && (GetKeyState(VK_CONTROL) & 0x8000 ) )
71 GetEditCtrl()->SetSel(0, -1);
72 return TRUE;
75 else if (pMsg->message == WM_MOUSEMOVE && this->m_bDyn )
77 if ((pMsg->wParam & MK_LBUTTON) == 0)
79 CPoint pt;
80 pt.x = LOWORD(pMsg->lParam);
81 pt.y = HIWORD(pMsg->lParam);
82 OnMouseMove((UINT)pMsg->wParam, pt);
83 return TRUE;
86 else if ((pMsg->message == WM_MOUSEWHEEL || pMsg->message == WM_MOUSEHWHEEL) && !GetDroppedState())
88 return TRUE;
91 return CComboBoxEx::PreTranslateMessage(pMsg);
94 BEGIN_MESSAGE_MAP(CHistoryCombo, CComboBoxEx)
95 ON_WM_MOUSEMOVE()
96 ON_WM_TIMER()
97 ON_WM_CREATE()
98 END_MESSAGE_MAP()
100 int CHistoryCombo::AddString(CString str, INT_PTR pos,BOOL isSel)
102 if (str.IsEmpty())
103 return -1;
105 COMBOBOXEXITEM cbei;
106 SecureZeroMemory(&cbei, sizeof cbei);
107 cbei.mask = CBEIF_TEXT;
109 if (pos < 0)
110 cbei.iItem = GetCount();
111 else
112 cbei.iItem = pos;
114 str.Trim(_T(" "));
115 CString combostring = str;
116 combostring.Replace('\r', ' ');
117 combostring.Replace('\n', ' ');
118 str=combostring=combostring.Trim();
119 cbei.pszText = const_cast<LPTSTR>(combostring.GetString());
121 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
122 if (m_bURLHistory)
124 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
125 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
127 if (str.Left(5) == _T("http:"))
128 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".html"));
129 else if (str.Left(6) == _T("https:"))
130 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".shtml"));
131 else if (str.Left(5) == _T("file:"))
132 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
133 else if (str.Left(4) == _T("git:"))
134 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
135 else if (str.Left(4) == _T("ssh:"))
136 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
137 else
138 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
140 cbei.iSelectedImage = cbei.iImage;
141 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
143 if (m_bPathHistory)
145 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
146 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
148 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
150 cbei.iSelectedImage = cbei.iImage;
151 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
153 #endif
155 //search the Combo for another string like this
156 //and do not insert if found
157 int nIndex = FindStringExact(-1, combostring);
158 if (nIndex != -1)
160 if (nIndex > cbei.iItem)
162 DeleteItem(nIndex);
163 m_arEntries.RemoveAt(nIndex);
165 else
167 if(isSel)
168 SetCurSel(nIndex);
169 return nIndex;
173 int nRet = InsertItem(&cbei);
174 if (nRet >= 0)
175 m_arEntries.InsertAt(nRet, str);
177 //truncate list to m_nMaxHistoryItems
178 int nNumItems = GetCount();
179 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
181 DeleteItem(m_nMaxHistoryItems);
182 m_arEntries.RemoveAt(m_nMaxHistoryItems);
185 if(isSel)
186 SetCurSel(nRet);
187 return nRet;
190 CString CHistoryCombo::LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix)
192 if (lpszSection == NULL || lpszKeyPrefix == NULL || *lpszSection == '\0')
193 return _T("");
195 m_sSection = lpszSection;
196 m_sKeyPrefix = lpszKeyPrefix;
198 int n = 0;
199 CString sText;
202 //keys are of form <lpszKeyPrefix><entrynumber>
203 CString sKey;
204 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n++);
205 sText = CRegString(sKey);
206 if (!sText.IsEmpty())
207 AddString(sText);
208 } while (!sText.IsEmpty() && n < m_nMaxHistoryItems);
210 SetCurSel(-1);
212 ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 0);
214 // need to resize the control for correct display
215 CRect rect;
216 GetWindowRect(rect);
217 GetParent()->ScreenToClient(rect);
218 MoveWindow(rect.left, rect.top, rect.Width(),100);
220 return sText;
223 void CHistoryCombo::SaveHistory()
225 if (m_sSection.IsEmpty())
226 return;
228 //add the current item to the history
229 CString sCurItem;
230 GetWindowText(sCurItem);
231 sCurItem.Trim();
232 if (!sCurItem.IsEmpty())
233 AddString(sCurItem, 0);
234 //save history to registry/inifile
235 int nMax = min(GetCount(), m_nMaxHistoryItems + 1);
236 for (int n = 0; n < nMax; n++)
238 CString sKey;
239 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
240 CRegString regkey = CRegString(sKey);
241 regkey = m_arEntries.GetAt(n);
243 //remove items exceeding the max number of history items
244 for (int n = nMax; ; n++)
246 CString sKey;
247 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
248 CRegString regkey = CRegString(sKey);
249 CString sText = regkey;
250 if (sText.IsEmpty())
251 break;
252 regkey.removeValue(); // remove entry
256 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries/*=TRUE*/)
258 ResetContent();
259 if (! m_sSection.IsEmpty() && bDeleteRegistryEntries)
261 //remove profile entries
262 CString sKey;
263 for (int n = 0; ; n++)
265 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
266 CRegString regkey = CRegString(sKey);
267 CString sText = regkey;
268 if (sText.IsEmpty())
269 break;
270 regkey.removeValue(); // remove entry
275 void CHistoryCombo::SetURLHistory(BOOL bURLHistory)
277 m_bURLHistory = bURLHistory;
279 if (m_bURLHistory)
281 HWND hwndEdit;
282 // use for ComboEx
283 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
284 if (NULL == hwndEdit)
286 // Try the unofficial way of getting the edit control CWnd*
287 CWnd* pWnd = this->GetDlgItem(1001);
288 if(pWnd)
290 hwndEdit = pWnd->GetSafeHwnd();
293 if (hwndEdit)
294 SHAutoComplete(hwndEdit, SHACF_URLALL);
297 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
298 SetImageList(&SYS_IMAGE_LIST());
299 #endif
302 void CHistoryCombo::SetPathHistory(BOOL bPathHistory)
304 m_bPathHistory = bPathHistory;
306 if (m_bPathHistory)
308 HWND hwndEdit;
309 // use for ComboEx
310 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
311 if (NULL == hwndEdit)
313 //if not, try the old standby
314 if(hwndEdit==NULL)
316 CWnd* pWnd = this->GetDlgItem(1001);
317 if(pWnd)
319 hwndEdit = pWnd->GetSafeHwnd();
323 if (hwndEdit)
324 SHAutoComplete(hwndEdit, SHACF_FILESYSTEM);
327 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
328 SetImageList(&SYS_IMAGE_LIST());
329 #endif
332 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems)
334 m_nMaxHistoryItems = nMaxItems;
336 //truncate list to nMaxItems
337 int nNumItems = GetCount();
338 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
339 DeleteString(m_nMaxHistoryItems);
341 void CHistoryCombo::AddString(STRING_VECTOR &list,BOOL isSel)
343 for(unsigned int i=0;i<list.size();i++)
345 AddString(list[i], -1, isSel);
348 CString CHistoryCombo::GetString() const
350 CString str;
351 int sel;
352 sel = GetCurSel();
353 DWORD style=GetStyle();
355 if (sel == CB_ERR ||(m_bURLHistory)||(m_bPathHistory) || (!(style&CBS_SIMPLE)))
357 GetWindowText(str);
358 return str;
361 return m_arEntries.GetAt(sel);
364 BOOL CHistoryCombo::RemoveSelectedItem()
366 int nIndex = GetCurSel();
367 if (nIndex == CB_ERR)
369 return FALSE;
372 DeleteItem(nIndex);
373 m_arEntries.RemoveAt(nIndex);
375 if ( nIndex < GetCount() )
377 // index stays the same to select the
378 // next item after the item which has
379 // just been deleted
381 else
383 // the end of the list has been reached
384 // so we select the previous item
385 nIndex--;
388 if ( nIndex == -1 )
390 // The one and only item has just been
391 // deleted -> reset window text since
392 // there is no item to select
393 SetWindowText(_T(""));
395 else
397 SetCurSel(nIndex);
400 // Since the dialog might be canceled we
401 // should now save the history. Before that
402 // set the selection to the first item so that
403 // the items will not be reordered and restore
404 // the selection after saving.
405 SetCurSel(0);
406 SaveHistory();
407 if ( nIndex != -1 )
409 SetCurSel(nIndex);
412 return TRUE;
415 void CHistoryCombo::PreSubclassWindow()
417 CComboBoxEx::PreSubclassWindow();
419 if (!m_bDyn)
420 CreateToolTip();
423 void CHistoryCombo::OnMouseMove(UINT nFlags, CPoint point)
425 CRect rectClient;
426 GetClientRect(&rectClient);
427 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
428 rectClient.right = rectClient.right - nComboButtonWidth;
430 if (rectClient.PtInRect(point))
432 ClientToScreen(&rectClient);
434 CString strText = GetString();
435 m_ToolInfo.lpszText = (LPTSTR)(LPCTSTR)strText;
437 HDC hDC = ::GetDC(m_hWnd);
439 CFont *pFont = GetFont();
440 HFONT hOldFont = (HFONT) ::SelectObject(hDC, (HFONT) *pFont);
442 SIZE size;
443 ::GetTextExtentPoint32(hDC, strText, strText.GetLength(), &size);
444 ::SelectObject(hDC, hOldFont);
445 ::ReleaseDC(m_hWnd, hDC);
447 if (size.cx > (rectClient.Width() - 6))
449 rectClient.left += 1;
450 rectClient.top += 3;
452 COLORREF rgbText = ::GetSysColor(COLOR_WINDOWTEXT);
453 COLORREF rgbBackground = ::GetSysColor(COLOR_WINDOW);
455 CWnd *pWnd = GetFocus();
456 if (pWnd)
458 if (pWnd->m_hWnd == m_hWnd)
460 rgbText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
461 rgbBackground = ::GetSysColor(COLOR_HIGHLIGHT);
465 if (!m_ttShown)
467 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, rgbBackground, 0);
468 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, rgbText, 0);
469 ::SendMessage(m_hWndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM) &m_ToolInfo);
470 ::SendMessage(m_hWndToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rectClient.left, rectClient.top));
471 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
472 SetTimer(1, 80, NULL);
473 SetTimer(2, 2000, NULL);
474 m_ttShown = TRUE;
477 else
479 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
480 m_ttShown = FALSE;
483 else
485 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
486 m_ttShown = FALSE;
489 CComboBoxEx::OnMouseMove(nFlags, point);
492 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent)
494 CPoint point;
495 DWORD ptW = GetMessagePos();
496 point.x = GET_X_LPARAM(ptW);
497 point.y = GET_Y_LPARAM(ptW);
498 ScreenToClient(&point);
500 CRect rectClient;
501 GetClientRect(&rectClient);
502 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
504 rectClient.right = rectClient.right - nComboButtonWidth;
506 if (!rectClient.PtInRect(point))
508 KillTimer(nIDEvent);
509 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
510 m_ttShown = FALSE;
512 if (nIDEvent == 2)
514 // tooltip timeout, just deactivate it
515 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
516 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again
517 // without the mouse pointer first leaving the control and entering it again
520 CComboBoxEx::OnTimer(nIDEvent);
523 void CHistoryCombo::CreateToolTip()
525 // create tooltip
526 m_hWndToolTip = ::CreateWindowEx(WS_EX_TOPMOST,
527 TOOLTIPS_CLASS,
528 NULL,
529 TTS_NOPREFIX | TTS_ALWAYSTIP,
530 CW_USEDEFAULT,
531 CW_USEDEFAULT,
532 CW_USEDEFAULT,
533 CW_USEDEFAULT,
534 m_hWnd,
535 NULL,
536 NULL,
537 NULL);
539 // initialize tool info struct
540 memset(&m_ToolInfo, 0, sizeof(m_ToolInfo));
541 m_ToolInfo.cbSize = sizeof(m_ToolInfo);
542 m_ToolInfo.uFlags = TTF_TRANSPARENT;
543 m_ToolInfo.hwnd = m_hWnd;
545 ::SendMessage(m_hWndToolTip, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
546 ::SendMessage(m_hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);
547 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, ::GetSysColor(COLOR_HIGHLIGHT), 0);
548 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, ::GetSysColor(COLOR_HIGHLIGHTTEXT), 0);
550 CRect rectMargins(0,-1,0,-1);
551 ::SendMessage(m_hWndToolTip, TTM_SETMARGIN, 0, (LPARAM)&rectMargins);
553 CFont *pFont = GetFont();
554 ::SendMessage(m_hWndToolTip, WM_SETFONT, (WPARAM)(HFONT)*pFont, FALSE);
557 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct)
559 if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)
560 return -1;
562 if (m_bDyn)
563 CreateToolTip();
565 return 0;