PushDlg: Fix: Wrong remote selected after selection via ref-browser.
[TortoiseGit.git] / src / Utils / MiscUI / HistoryCombo.cpp
blob9040a5f64280e3cef2c69c3ede499efa21a8848c
1 // TortoiseSVN - 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;
40 CHistoryCombo::~CHistoryCombo()
44 BOOL CHistoryCombo::PreCreateWindow(CREATESTRUCT& cs)
46 if (!m_bAllowSortStyle) //turn off CBS_SORT style
47 cs.style &= ~CBS_SORT;
48 cs.style |= CBS_AUTOHSCROLL;
49 m_bDyn = TRUE;
50 return CComboBoxEx::PreCreateWindow(cs);
53 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 (pMsg->message == WM_MOUSEMOVE && this->m_bDyn )
71 if ((pMsg->wParam & MK_LBUTTON) == 0)
73 CPoint pt;
74 pt.x = LOWORD(pMsg->lParam);
75 pt.y = HIWORD(pMsg->lParam);
76 OnMouseMove(pMsg->wParam, pt);
77 return TRUE;
81 return CComboBoxEx::PreTranslateMessage(pMsg);
84 BEGIN_MESSAGE_MAP(CHistoryCombo, CComboBoxEx)
85 ON_WM_MOUSEMOVE()
86 ON_WM_TIMER()
87 ON_WM_CREATE()
88 END_MESSAGE_MAP()
90 int CHistoryCombo::AddString(CString str, INT_PTR pos)
92 if (str.IsEmpty())
93 return -1;
95 COMBOBOXEXITEM cbei;
96 SecureZeroMemory(&cbei, sizeof cbei);
97 cbei.mask = CBEIF_TEXT;
99 if (pos < 0)
100 cbei.iItem = GetCount();
101 else
102 cbei.iItem = pos;
104 str.Trim(_T(" "));
105 CString combostring = str;
106 combostring.Replace('\r', ' ');
107 combostring.Replace('\n', ' ');
108 cbei.pszText = const_cast<LPTSTR>(combostring.GetString());
110 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
111 if (m_bURLHistory)
113 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
114 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
116 if (str.Left(5) == _T("http:"))
117 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".html"));
118 else if (str.Left(6) == _T("https:"))
119 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(_T(".shtml"));
120 else if (str.Left(5) == _T("file:"))
121 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
122 else if (str.Left(4) == _T("git:"))
123 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
124 else if (str.Left(4) == _T("ssh:"))
125 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
126 else
127 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
129 cbei.iSelectedImage = cbei.iImage;
130 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
132 if (m_bPathHistory)
134 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);
135 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())
137 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();
139 cbei.iSelectedImage = cbei.iImage;
140 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
142 #endif
143 int nRet = InsertItem(&cbei);
144 if (nRet >= 0)
145 m_arEntries.InsertAt(nRet, str);
147 //search the Combo for another string like this
148 //and delete it if one is found
149 str.Trim();
150 int nIndex = FindStringExact(0, combostring);
151 if (nIndex != -1 && nIndex != nRet)
153 DeleteItem(nIndex);
154 m_arEntries.RemoveAt(nIndex);
155 //nRet is now (potentially) invalid. Reset it.
156 nRet = FindStringExact(0, str);
159 //truncate list to m_nMaxHistoryItems
160 int nNumItems = GetCount();
161 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
163 DeleteItem(m_nMaxHistoryItems);
164 m_arEntries.RemoveAt(m_nMaxHistoryItems);
167 SetCurSel(nRet);
168 return nRet;
171 CString CHistoryCombo::LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix)
173 if (lpszSection == NULL || lpszKeyPrefix == NULL || *lpszSection == '\0')
174 return _T("");
176 m_sSection = lpszSection;
177 m_sKeyPrefix = lpszKeyPrefix;
179 int n = 0;
180 CString sText;
183 //keys are of form <lpszKeyPrefix><entrynumber>
184 CString sKey;
185 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n++);
186 sText = CRegString(sKey);
187 if (!sText.IsEmpty())
188 AddString(sText);
189 } while (!sText.IsEmpty() && n < m_nMaxHistoryItems);
191 SetCurSel(-1);
193 ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 0);
195 // need to resize the control for correct display
196 CRect rect;
197 GetWindowRect(rect);
198 GetParent()->ScreenToClient(rect);
199 MoveWindow(rect.left, rect.top, rect.Width(),100);
201 return sText;
204 void CHistoryCombo::SaveHistory()
206 if (m_sSection.IsEmpty())
207 return;
209 //add the current item to the history
210 CString sCurItem;
211 GetWindowText(sCurItem);
212 sCurItem.Trim();
213 if (!sCurItem.IsEmpty())
214 AddString(sCurItem, 0);
215 //save history to registry/inifile
216 int nMax = min(GetCount(), m_nMaxHistoryItems + 1);
217 for (int n = 0; n < nMax; n++)
219 CString sKey;
220 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
221 CRegString regkey = CRegString(sKey);
222 regkey = m_arEntries.GetAt(n);
224 //remove items exceeding the max number of history items
225 for (int n = nMax; ; n++)
227 CString sKey;
228 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
229 CRegString regkey = CRegString(sKey);
230 CString sText = regkey;
231 if (sText.IsEmpty())
232 break;
233 regkey.removeValue(); // remove entry
237 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries/*=TRUE*/)
239 ResetContent();
240 if (! m_sSection.IsEmpty() && bDeleteRegistryEntries)
242 //remove profile entries
243 CString sKey;
244 for (int n = 0; ; n++)
246 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);
247 CRegString regkey = CRegString(sKey);
248 CString sText = regkey;
249 if (sText.IsEmpty())
250 break;
251 regkey.removeValue(); // remove entry
256 void CHistoryCombo::SetURLHistory(BOOL bURLHistory)
258 m_bURLHistory = bURLHistory;
260 if (m_bURLHistory)
262 HWND hwndEdit;
263 // use for ComboEx
264 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
265 if (NULL == hwndEdit)
267 // Try the unofficial way of getting the edit control CWnd*
268 CWnd* pWnd = this->GetDlgItem(1001);
269 if(pWnd)
271 hwndEdit = pWnd->GetSafeHwnd();
274 if (hwndEdit)
275 SHAutoComplete(hwndEdit, SHACF_URLALL);
278 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
279 SetImageList(&SYS_IMAGE_LIST());
280 #endif
283 void CHistoryCombo::SetPathHistory(BOOL bPathHistory)
285 m_bPathHistory = bPathHistory;
287 if (m_bPathHistory)
289 HWND hwndEdit;
290 // use for ComboEx
291 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);
292 if (NULL == hwndEdit)
294 //if not, try the old standby
295 if(hwndEdit==NULL)
297 CWnd* pWnd = this->GetDlgItem(1001);
298 if(pWnd)
300 hwndEdit = pWnd->GetSafeHwnd();
304 if (hwndEdit)
305 SHAutoComplete(hwndEdit, SHACF_FILESYSTEM);
308 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
309 SetImageList(&SYS_IMAGE_LIST());
310 #endif
313 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems)
315 m_nMaxHistoryItems = nMaxItems;
317 //truncate list to nMaxItems
318 int nNumItems = GetCount();
319 for (int n = m_nMaxHistoryItems; n < nNumItems; n++)
320 DeleteString(m_nMaxHistoryItems);
322 void CHistoryCombo::AddString(STRING_VECTOR &list)
324 for(unsigned int i=0;i<list.size();i++)
326 AddString(list[i]);
329 CString CHistoryCombo::GetString() const
331 CString str;
332 int sel;
333 sel = GetCurSel();
334 DWORD style=GetStyle();
336 if (sel == CB_ERR)
338 GetWindowText(str);
339 return str;
342 if ((m_bURLHistory)||(m_bPathHistory) || (!(style&CBS_SIMPLE)) )
344 //URL and path history combo boxes are editable, so get
345 //the string directly from the combobox
346 GetLBText(sel, str.GetBuffer(GetLBTextLen(sel)));
347 str.ReleaseBuffer();
348 return str;
350 return m_arEntries.GetAt(sel);
353 BOOL CHistoryCombo::RemoveSelectedItem()
355 int nIndex = GetCurSel();
356 if (nIndex == CB_ERR)
358 return FALSE;
361 DeleteItem(nIndex);
362 m_arEntries.RemoveAt(nIndex);
364 if ( nIndex < GetCount() )
366 // index stays the same to select the
367 // next item after the item which has
368 // just been deleted
370 else
372 // the end of the list has been reached
373 // so we select the previous item
374 nIndex--;
377 if ( nIndex == -1 )
379 // The one and only item has just been
380 // deleted -> reset window text since
381 // there is no item to select
382 SetWindowText(_T(""));
384 else
386 SetCurSel(nIndex);
389 // Since the dialog might be canceled we
390 // should now save the history. Before that
391 // set the selection to the first item so that
392 // the items will not be reordered and restore
393 // the selection after saving.
394 SetCurSel(0);
395 SaveHistory();
396 if ( nIndex != -1 )
398 SetCurSel(nIndex);
401 return TRUE;
404 void CHistoryCombo::PreSubclassWindow()
406 CComboBoxEx::PreSubclassWindow();
408 if (!m_bDyn)
409 CreateToolTip();
412 void CHistoryCombo::OnMouseMove(UINT nFlags, CPoint point)
414 CRect rectClient;
415 GetClientRect(&rectClient);
416 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
417 rectClient.right = rectClient.right - nComboButtonWidth;
419 if (rectClient.PtInRect(point))
421 ClientToScreen(&rectClient);
423 CString strText = GetString();
424 m_ToolInfo.lpszText = (LPTSTR)(LPCTSTR)strText;
426 HDC hDC = ::GetDC(m_hWnd);
428 CFont *pFont = GetFont();
429 HFONT hOldFont = (HFONT) ::SelectObject(hDC, (HFONT) *pFont);
431 SIZE size;
432 ::GetTextExtentPoint32(hDC, strText, strText.GetLength(), &size);
433 ::SelectObject(hDC, hOldFont);
434 ::ReleaseDC(m_hWnd, hDC);
436 if (size.cx > (rectClient.Width() - 6))
438 rectClient.left += 1;
439 rectClient.top += 3;
441 COLORREF rgbText = ::GetSysColor(COLOR_WINDOWTEXT);
442 COLORREF rgbBackground = ::GetSysColor(COLOR_WINDOW);
444 CWnd *pWnd = GetFocus();
445 if (pWnd)
447 if (pWnd->m_hWnd == m_hWnd)
449 rgbText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
450 rgbBackground = ::GetSysColor(COLOR_HIGHLIGHT);
454 if (!m_ttShown)
456 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, rgbBackground, 0);
457 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, rgbText, 0);
458 ::SendMessage(m_hWndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM) &m_ToolInfo);
459 ::SendMessage(m_hWndToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rectClient.left, rectClient.top));
460 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
461 SetTimer(1, 80, NULL);
462 SetTimer(2, 2000, NULL);
463 m_ttShown = TRUE;
466 else
468 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
469 m_ttShown = FALSE;
472 else
474 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
475 m_ttShown = FALSE;
478 CComboBoxEx::OnMouseMove(nFlags, point);
481 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent)
483 CPoint point;
484 DWORD ptW = GetMessagePos();
485 point.x = GET_X_LPARAM(ptW);
486 point.y = GET_Y_LPARAM(ptW);
487 ScreenToClient(&point);
489 CRect rectClient;
490 GetClientRect(&rectClient);
491 int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;
493 rectClient.right = rectClient.right - nComboButtonWidth;
495 if (!rectClient.PtInRect(point))
497 KillTimer(nIDEvent);
498 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
499 m_ttShown = FALSE;
501 if (nIDEvent == 2)
503 // tooltip timeout, just deactivate it
504 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);
505 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again
506 // without the mouse pointer first leaving the control and entering it again
509 CComboBoxEx::OnTimer(nIDEvent);
512 void CHistoryCombo::CreateToolTip()
514 // create tooltip
515 m_hWndToolTip = ::CreateWindowEx(WS_EX_TOPMOST,
516 TOOLTIPS_CLASS,
517 NULL,
518 TTS_NOPREFIX | TTS_ALWAYSTIP,
519 CW_USEDEFAULT,
520 CW_USEDEFAULT,
521 CW_USEDEFAULT,
522 CW_USEDEFAULT,
523 m_hWnd,
524 NULL,
525 NULL,
526 NULL);
528 // initialize tool info struct
529 memset(&m_ToolInfo, 0, sizeof(m_ToolInfo));
530 m_ToolInfo.cbSize = sizeof(m_ToolInfo);
531 m_ToolInfo.uFlags = TTF_TRANSPARENT;
532 m_ToolInfo.hwnd = m_hWnd;
534 ::SendMessage(m_hWndToolTip, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
535 ::SendMessage(m_hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);
536 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, ::GetSysColor(COLOR_HIGHLIGHT), 0);
537 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, ::GetSysColor(COLOR_HIGHLIGHTTEXT), 0);
539 CRect rectMargins(0,-1,0,-1);
540 ::SendMessage(m_hWndToolTip, TTM_SETMARGIN, 0, (LPARAM)&rectMargins);
542 CFont *pFont = GetFont();
543 ::SendMessage(m_hWndToolTip, WM_SETFONT, (WPARAM)(HFONT)*pFont, FALSE);
546 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct)
548 if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)
549 return -1;
551 if (m_bDyn)
552 CreateToolTip();
554 return 0;