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.
20 #include "HistoryCombo.h"
21 #include "../registry.h"
23 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
24 #include "../SysImageList.h"
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
;
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
;
51 return CComboBoxEx::PreCreateWindow(cs
);
54 BOOL
CHistoryCombo::PreTranslateMessage(MSG
* pMsg
)
57 if (pMsg
->message
== WM_KEYDOWN
)
59 bool bShift
= !!(GetKeyState(VK_SHIFT
) & 0x8000);
60 int nVirtKey
= (int) pMsg
->wParam
;
62 if (nVirtKey
== VK_RETURN
)
63 return OnReturnKeyPressed();
64 else if (nVirtKey
== VK_DELETE
&& bShift
&& GetDroppedState() )
70 if (nVirtKey
== 'A' && (GetKeyState(VK_CONTROL
) & 0x8000 ) )
72 GetEditCtrl()->SetSel(0, -1);
76 if (pMsg
->message
== WM_MOUSEMOVE
&& this->m_bDyn
)
78 if ((pMsg
->wParam
& MK_LBUTTON
) == 0)
81 pt
.x
= LOWORD(pMsg
->lParam
);
82 pt
.y
= HIWORD(pMsg
->lParam
);
83 OnMouseMove(pMsg
->wParam
, pt
);
88 return CComboBoxEx::PreTranslateMessage(pMsg
);
91 BEGIN_MESSAGE_MAP(CHistoryCombo
, CComboBoxEx
)
97 int CHistoryCombo::AddString(CString str
, INT_PTR pos
,BOOL isSel
)
103 SecureZeroMemory(&cbei
, sizeof cbei
);
104 cbei
.mask
= CBEIF_TEXT
;
107 cbei
.iItem
= GetCount();
112 CString combostring
= str
;
113 combostring
.Replace('\r', ' ');
114 combostring
.Replace('\n', ' ');
115 str
=combostring
=combostring
.Trim();
116 cbei
.pszText
= const_cast<LPTSTR
>(combostring
.GetString());
118 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
121 cbei
.iImage
= SYS_IMAGE_LIST().GetFileIconIndex(str
);
122 if (cbei
.iImage
== SYS_IMAGE_LIST().GetDefaultIconIndex())
124 if (str
.Left(5) == _T("http:"))
125 cbei
.iImage
= SYS_IMAGE_LIST().GetFileIconIndex(_T(".html"));
126 else if (str
.Left(6) == _T("https:"))
127 cbei
.iImage
= SYS_IMAGE_LIST().GetFileIconIndex(_T(".shtml"));
128 else if (str
.Left(5) == _T("file:"))
129 cbei
.iImage
= SYS_IMAGE_LIST().GetDirIconIndex();
130 else if (str
.Left(4) == _T("git:"))
131 cbei
.iImage
= SYS_IMAGE_LIST().GetDirIconIndex();
132 else if (str
.Left(4) == _T("ssh:"))
133 cbei
.iImage
= SYS_IMAGE_LIST().GetDirIconIndex();
135 cbei
.iImage
= SYS_IMAGE_LIST().GetDirIconIndex();
137 cbei
.iSelectedImage
= cbei
.iImage
;
138 cbei
.mask
|= CBEIF_IMAGE
| CBEIF_SELECTEDIMAGE
;
142 cbei
.iImage
= SYS_IMAGE_LIST().GetFileIconIndex(str
);
143 if (cbei
.iImage
== SYS_IMAGE_LIST().GetDefaultIconIndex())
145 cbei
.iImage
= SYS_IMAGE_LIST().GetDirIconIndex();
147 cbei
.iSelectedImage
= cbei
.iImage
;
148 cbei
.mask
|= CBEIF_IMAGE
| CBEIF_SELECTEDIMAGE
;
151 int nRet
= InsertItem(&cbei
);
153 m_arEntries
.InsertAt(nRet
, str
);
155 //search the Combo for another string like this
156 //and delete it if one is found
158 int nIndex
= FindStringExact(-1, combostring
);
159 if (nIndex
!= -1 && nIndex
!= nRet
)
162 m_arEntries
.RemoveAt(nIndex
);
163 //nRet is now (potentially) invalid. Reset it.
164 nRet
= FindStringExact(-1, str
);
167 //truncate list to m_nMaxHistoryItems
168 int nNumItems
= GetCount();
169 for (int n
= m_nMaxHistoryItems
; n
< nNumItems
; n
++)
171 DeleteItem(m_nMaxHistoryItems
);
172 m_arEntries
.RemoveAt(m_nMaxHistoryItems
);
180 CString
CHistoryCombo::LoadHistory(LPCTSTR lpszSection
, LPCTSTR lpszKeyPrefix
)
182 if (lpszSection
== NULL
|| lpszKeyPrefix
== NULL
|| *lpszSection
== '\0')
185 m_sSection
= lpszSection
;
186 m_sKeyPrefix
= lpszKeyPrefix
;
192 //keys are of form <lpszKeyPrefix><entrynumber>
194 sKey
.Format(_T("%s\\%s%d"), (LPCTSTR
)m_sSection
, (LPCTSTR
)m_sKeyPrefix
, n
++);
195 sText
= CRegString(sKey
);
196 if (!sText
.IsEmpty())
198 } while (!sText
.IsEmpty() && n
< m_nMaxHistoryItems
);
202 ModifyStyleEx(WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
, 0);
204 // need to resize the control for correct display
207 GetParent()->ScreenToClient(rect
);
208 MoveWindow(rect
.left
, rect
.top
, rect
.Width(),100);
213 void CHistoryCombo::SaveHistory()
215 if (m_sSection
.IsEmpty())
218 //add the current item to the history
220 GetWindowText(sCurItem
);
222 if (!sCurItem
.IsEmpty())
223 AddString(sCurItem
, 0);
224 //save history to registry/inifile
225 int nMax
= min(GetCount(), m_nMaxHistoryItems
+ 1);
226 for (int n
= 0; n
< nMax
; n
++)
229 sKey
.Format(_T("%s\\%s%d"), (LPCTSTR
)m_sSection
, (LPCTSTR
)m_sKeyPrefix
, n
);
230 CRegString regkey
= CRegString(sKey
);
231 regkey
= m_arEntries
.GetAt(n
);
233 //remove items exceeding the max number of history items
234 for (int n
= nMax
; ; n
++)
237 sKey
.Format(_T("%s\\%s%d"), (LPCTSTR
)m_sSection
, (LPCTSTR
)m_sKeyPrefix
, n
);
238 CRegString regkey
= CRegString(sKey
);
239 CString sText
= regkey
;
242 regkey
.removeValue(); // remove entry
246 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries
/*=TRUE*/)
249 if (! m_sSection
.IsEmpty() && bDeleteRegistryEntries
)
251 //remove profile entries
253 for (int n
= 0; ; n
++)
255 sKey
.Format(_T("%s\\%s%d"), (LPCTSTR
)m_sSection
, (LPCTSTR
)m_sKeyPrefix
, n
);
256 CRegString regkey
= CRegString(sKey
);
257 CString sText
= regkey
;
260 regkey
.removeValue(); // remove entry
265 void CHistoryCombo::SetURLHistory(BOOL bURLHistory
)
267 m_bURLHistory
= bURLHistory
;
273 hwndEdit
= (HWND
)::SendMessage(this->m_hWnd
, CBEM_GETEDITCONTROL
, 0, 0);
274 if (NULL
== hwndEdit
)
276 // Try the unofficial way of getting the edit control CWnd*
277 CWnd
* pWnd
= this->GetDlgItem(1001);
280 hwndEdit
= pWnd
->GetSafeHwnd();
284 SHAutoComplete(hwndEdit
, SHACF_URLALL
);
287 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
288 SetImageList(&SYS_IMAGE_LIST());
292 void CHistoryCombo::SetPathHistory(BOOL bPathHistory
)
294 m_bPathHistory
= bPathHistory
;
300 hwndEdit
= (HWND
)::SendMessage(this->m_hWnd
, CBEM_GETEDITCONTROL
, 0, 0);
301 if (NULL
== hwndEdit
)
303 //if not, try the old standby
306 CWnd
* pWnd
= this->GetDlgItem(1001);
309 hwndEdit
= pWnd
->GetSafeHwnd();
314 SHAutoComplete(hwndEdit
, SHACF_FILESYSTEM
);
317 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST
318 SetImageList(&SYS_IMAGE_LIST());
322 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems
)
324 m_nMaxHistoryItems
= nMaxItems
;
326 //truncate list to nMaxItems
327 int nNumItems
= GetCount();
328 for (int n
= m_nMaxHistoryItems
; n
< nNumItems
; n
++)
329 DeleteString(m_nMaxHistoryItems
);
331 void CHistoryCombo::AddString(STRING_VECTOR
&list
,BOOL isSel
)
333 for(unsigned int i
=0;i
<list
.size();i
++)
335 AddString(list
[i
], -1, isSel
);
338 CString
CHistoryCombo::GetString() const
343 DWORD style
=GetStyle();
351 if ((m_bURLHistory
)||(m_bPathHistory
) || (!(style
&CBS_SIMPLE
)) )
353 //URL and path history combo boxes are editable, so get
354 //the string directly from the combobox
355 GetLBText(sel
, str
.GetBuffer(GetLBTextLen(sel
)));
359 return m_arEntries
.GetAt(sel
);
362 BOOL
CHistoryCombo::RemoveSelectedItem()
364 int nIndex
= GetCurSel();
365 if (nIndex
== CB_ERR
)
371 m_arEntries
.RemoveAt(nIndex
);
373 if ( nIndex
< GetCount() )
375 // index stays the same to select the
376 // next item after the item which has
381 // the end of the list has been reached
382 // so we select the previous item
388 // The one and only item has just been
389 // deleted -> reset window text since
390 // there is no item to select
391 SetWindowText(_T(""));
398 // Since the dialog might be canceled we
399 // should now save the history. Before that
400 // set the selection to the first item so that
401 // the items will not be reordered and restore
402 // the selection after saving.
413 void CHistoryCombo::PreSubclassWindow()
415 CComboBoxEx::PreSubclassWindow();
421 void CHistoryCombo::OnMouseMove(UINT nFlags
, CPoint point
)
424 GetClientRect(&rectClient
);
425 int nComboButtonWidth
= ::GetSystemMetrics(SM_CXHTHUMB
) + 2;
426 rectClient
.right
= rectClient
.right
- nComboButtonWidth
;
428 if (rectClient
.PtInRect(point
))
430 ClientToScreen(&rectClient
);
432 CString strText
= GetString();
433 m_ToolInfo
.lpszText
= (LPTSTR
)(LPCTSTR
)strText
;
435 HDC hDC
= ::GetDC(m_hWnd
);
437 CFont
*pFont
= GetFont();
438 HFONT hOldFont
= (HFONT
) ::SelectObject(hDC
, (HFONT
) *pFont
);
441 ::GetTextExtentPoint32(hDC
, strText
, strText
.GetLength(), &size
);
442 ::SelectObject(hDC
, hOldFont
);
443 ::ReleaseDC(m_hWnd
, hDC
);
445 if (size
.cx
> (rectClient
.Width() - 6))
447 rectClient
.left
+= 1;
450 COLORREF rgbText
= ::GetSysColor(COLOR_WINDOWTEXT
);
451 COLORREF rgbBackground
= ::GetSysColor(COLOR_WINDOW
);
453 CWnd
*pWnd
= GetFocus();
456 if (pWnd
->m_hWnd
== m_hWnd
)
458 rgbText
= ::GetSysColor(COLOR_HIGHLIGHTTEXT
);
459 rgbBackground
= ::GetSysColor(COLOR_HIGHLIGHT
);
465 ::SendMessage(m_hWndToolTip
, TTM_SETTIPBKCOLOR
, rgbBackground
, 0);
466 ::SendMessage(m_hWndToolTip
, TTM_SETTIPTEXTCOLOR
, rgbText
, 0);
467 ::SendMessage(m_hWndToolTip
, TTM_UPDATETIPTEXT
, 0, (LPARAM
) &m_ToolInfo
);
468 ::SendMessage(m_hWndToolTip
, TTM_TRACKPOSITION
, 0, (LPARAM
)MAKELONG(rectClient
.left
, rectClient
.top
));
469 ::SendMessage(m_hWndToolTip
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)(LPTOOLINFO
) &m_ToolInfo
);
470 SetTimer(1, 80, NULL
);
471 SetTimer(2, 2000, NULL
);
477 ::SendMessage(m_hWndToolTip
, TTM_TRACKACTIVATE
, FALSE
, (LPARAM
)(LPTOOLINFO
) &m_ToolInfo
);
483 ::SendMessage(m_hWndToolTip
, TTM_TRACKACTIVATE
, FALSE
, (LPARAM
)(LPTOOLINFO
) &m_ToolInfo
);
487 CComboBoxEx::OnMouseMove(nFlags
, point
);
490 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent
)
493 DWORD ptW
= GetMessagePos();
494 point
.x
= GET_X_LPARAM(ptW
);
495 point
.y
= GET_Y_LPARAM(ptW
);
496 ScreenToClient(&point
);
499 GetClientRect(&rectClient
);
500 int nComboButtonWidth
= ::GetSystemMetrics(SM_CXHTHUMB
) + 2;
502 rectClient
.right
= rectClient
.right
- nComboButtonWidth
;
504 if (!rectClient
.PtInRect(point
))
507 ::SendMessage(m_hWndToolTip
, TTM_TRACKACTIVATE
, FALSE
, (LPARAM
)(LPTOOLINFO
) &m_ToolInfo
);
512 // tooltip timeout, just deactivate it
513 ::SendMessage(m_hWndToolTip
, TTM_TRACKACTIVATE
, FALSE
, (LPARAM
)(LPTOOLINFO
) &m_ToolInfo
);
514 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again
515 // without the mouse pointer first leaving the control and entering it again
518 CComboBoxEx::OnTimer(nIDEvent
);
521 void CHistoryCombo::CreateToolTip()
524 m_hWndToolTip
= ::CreateWindowEx(WS_EX_TOPMOST
,
527 TTS_NOPREFIX
| TTS_ALWAYSTIP
,
537 // initialize tool info struct
538 memset(&m_ToolInfo
, 0, sizeof(m_ToolInfo
));
539 m_ToolInfo
.cbSize
= sizeof(m_ToolInfo
);
540 m_ToolInfo
.uFlags
= TTF_TRANSPARENT
;
541 m_ToolInfo
.hwnd
= m_hWnd
;
543 ::SendMessage(m_hWndToolTip
, TTM_SETMAXTIPWIDTH
, 0, SHRT_MAX
);
544 ::SendMessage(m_hWndToolTip
, TTM_ADDTOOL
, 0, (LPARAM
) (LPTOOLINFO
) &m_ToolInfo
);
545 ::SendMessage(m_hWndToolTip
, TTM_SETTIPBKCOLOR
, ::GetSysColor(COLOR_HIGHLIGHT
), 0);
546 ::SendMessage(m_hWndToolTip
, TTM_SETTIPTEXTCOLOR
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
), 0);
548 CRect
rectMargins(0,-1,0,-1);
549 ::SendMessage(m_hWndToolTip
, TTM_SETMARGIN
, 0, (LPARAM
)&rectMargins
);
551 CFont
*pFont
= GetFont();
552 ::SendMessage(m_hWndToolTip
, WM_SETFONT
, (WPARAM
)(HFONT
)*pFont
, FALSE
);
555 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct
)
557 if (CComboBoxEx::OnCreate(lpCreateStruct
) == -1)