OSDN Git Service

PushDlg: Fix: Wrong remote selected after selection via ref-browser.
[tortoisegit/TortoiseGitJp.git] / src / Utils / MiscUI / HistoryCombo.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \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
9 \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
14 \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
18 //\r
19 #include "stdafx.h"\r
20 #include "HistoryCombo.h"\r
21 #include "../registry.h"\r
22 \r
23 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
24 #include "../SysImageList.h"\r
25 #endif\r
26 \r
27 #define MAX_HISTORY_ITEMS 25\r
28 \r
29 CHistoryCombo::CHistoryCombo(BOOL bAllowSortStyle /*=FALSE*/ )\r
30 {\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
36         m_ttShown = FALSE;\r
37         m_bDyn = FALSE;\r
38 }\r
39 \r
40 CHistoryCombo::~CHistoryCombo()\r
41 {\r
42 }\r
43 \r
44 BOOL CHistoryCombo::PreCreateWindow(CREATESTRUCT& cs) \r
45 {\r
46         if (!m_bAllowSortStyle)  //turn off CBS_SORT style\r
47                 cs.style &= ~CBS_SORT;\r
48         cs.style |= CBS_AUTOHSCROLL;\r
49         m_bDyn = TRUE;\r
50         return CComboBoxEx::PreCreateWindow(cs);\r
51 }\r
52 \r
53 BOOL CHistoryCombo::PreTranslateMessage(MSG* pMsg)\r
54 {\r
55 \r
56         if (pMsg->message == WM_KEYDOWN)\r
57         {\r
58                 bool bShift = !!(GetKeyState(VK_SHIFT) & 0x8000);\r
59                 int nVirtKey = (int) pMsg->wParam;\r
60                 \r
61                 if (nVirtKey == VK_RETURN)\r
62                         return OnReturnKeyPressed();\r
63                 else if (nVirtKey == VK_DELETE && bShift && GetDroppedState() )\r
64                 {\r
65                         RemoveSelectedItem();\r
66                         return TRUE;\r
67                 }\r
68         }\r
69         if (pMsg->message == WM_MOUSEMOVE && this->m_bDyn ) \r
70         {\r
71                 if ((pMsg->wParam & MK_LBUTTON) == 0)\r
72                 {\r
73                         CPoint pt;\r
74                         pt.x = LOWORD(pMsg->lParam);\r
75                         pt.y = HIWORD(pMsg->lParam);\r
76                         OnMouseMove(pMsg->wParam, pt);\r
77                         return TRUE;\r
78                 }\r
79         }\r
80 \r
81         return CComboBoxEx::PreTranslateMessage(pMsg);\r
82 }\r
83 \r
84 BEGIN_MESSAGE_MAP(CHistoryCombo, CComboBoxEx)\r
85         ON_WM_MOUSEMOVE()\r
86         ON_WM_TIMER()\r
87         ON_WM_CREATE()\r
88 END_MESSAGE_MAP()\r
89 \r
90 int CHistoryCombo::AddString(CString str, INT_PTR pos)\r
91 {\r
92         if (str.IsEmpty())\r
93                 return -1;\r
94         \r
95         COMBOBOXEXITEM cbei;\r
96         SecureZeroMemory(&cbei, sizeof cbei);\r
97         cbei.mask = CBEIF_TEXT;\r
98 \r
99         if (pos < 0)\r
100         cbei.iItem = GetCount();\r
101         else\r
102                 cbei.iItem = pos;\r
103 \r
104         str.Trim(_T(" "));\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
109 \r
110 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
111         if (m_bURLHistory)\r
112         {\r
113                 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);\r
114                 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())\r
115                 {\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
126                         else\r
127                                 cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();\r
128                 }\r
129                 cbei.iSelectedImage = cbei.iImage;\r
130                 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;\r
131         }\r
132         if (m_bPathHistory)\r
133         {\r
134                 cbei.iImage = SYS_IMAGE_LIST().GetFileIconIndex(str);\r
135                 if (cbei.iImage == SYS_IMAGE_LIST().GetDefaultIconIndex())\r
136                 {\r
137                         cbei.iImage = SYS_IMAGE_LIST().GetDirIconIndex();\r
138                 }\r
139                 cbei.iSelectedImage = cbei.iImage;\r
140                 cbei.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;\r
141         }\r
142 #endif\r
143         int nRet = InsertItem(&cbei);\r
144         if (nRet >= 0)\r
145                 m_arEntries.InsertAt(nRet, str);\r
146 \r
147         //search the Combo for another string like this\r
148         //and delete it if one is found\r
149         str.Trim();\r
150         int nIndex = FindStringExact(0, combostring);\r
151         if (nIndex != -1 && nIndex != nRet)\r
152         {\r
153                 DeleteItem(nIndex);\r
154                 m_arEntries.RemoveAt(nIndex);\r
155                 //nRet is now (potentially) invalid. Reset it.\r
156                 nRet = FindStringExact(0, str);\r
157         }\r
158 \r
159         //truncate list to m_nMaxHistoryItems\r
160         int nNumItems = GetCount();\r
161         for (int n = m_nMaxHistoryItems; n < nNumItems; n++)\r
162         {\r
163                 DeleteItem(m_nMaxHistoryItems);\r
164                 m_arEntries.RemoveAt(m_nMaxHistoryItems);\r
165         }\r
166 \r
167         SetCurSel(nRet);\r
168         return nRet;\r
169 }\r
170 \r
171 CString CHistoryCombo::LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix)\r
172 {\r
173         if (lpszSection == NULL || lpszKeyPrefix == NULL || *lpszSection == '\0')\r
174                 return _T("");\r
175 \r
176         m_sSection = lpszSection;\r
177         m_sKeyPrefix = lpszKeyPrefix;\r
178 \r
179         int n = 0;\r
180         CString sText;\r
181         do\r
182         {\r
183                 //keys are of form <lpszKeyPrefix><entrynumber>\r
184                 CString sKey;\r
185                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n++);\r
186                 sText = CRegString(sKey);\r
187                 if (!sText.IsEmpty())\r
188                         AddString(sText);\r
189         } while (!sText.IsEmpty() && n < m_nMaxHistoryItems);\r
190 \r
191         SetCurSel(-1);\r
192 \r
193         ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 0);\r
194 \r
195         // need to resize the control for correct display\r
196         CRect rect;\r
197         GetWindowRect(rect);\r
198         GetParent()->ScreenToClient(rect);\r
199         MoveWindow(rect.left, rect.top, rect.Width(),100);\r
200 \r
201         return sText;\r
202 }\r
203 \r
204 void CHistoryCombo::SaveHistory()\r
205 {\r
206         if (m_sSection.IsEmpty())\r
207                 return;\r
208 \r
209         //add the current item to the history\r
210         CString sCurItem;\r
211         GetWindowText(sCurItem);\r
212         sCurItem.Trim();\r
213         if (!sCurItem.IsEmpty())\r
214                 AddString(sCurItem, 0);\r
215         //save history to registry/inifile\r
216         int nMax = min(GetCount(), m_nMaxHistoryItems + 1);\r
217         for (int n = 0; n < nMax; n++)\r
218         {\r
219                 CString sKey;\r
220                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
221                 CRegString regkey = CRegString(sKey);\r
222                 regkey = m_arEntries.GetAt(n);\r
223         }\r
224         //remove items exceeding the max number of history items\r
225         for (int n = nMax; ; n++)\r
226         {\r
227                 CString sKey;\r
228                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
229                 CRegString regkey = CRegString(sKey);\r
230                 CString sText = regkey;\r
231                 if (sText.IsEmpty())\r
232                         break;\r
233                 regkey.removeValue(); // remove entry\r
234         }\r
235 }\r
236 \r
237 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries/*=TRUE*/)\r
238 {\r
239         ResetContent();\r
240         if (! m_sSection.IsEmpty() && bDeleteRegistryEntries)\r
241         {\r
242                 //remove profile entries\r
243                 CString sKey;\r
244                 for (int n = 0; ; n++)\r
245                 {\r
246                         sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
247                         CRegString regkey = CRegString(sKey);\r
248                         CString sText = regkey;\r
249                         if (sText.IsEmpty())\r
250                                 break;\r
251                         regkey.removeValue(); // remove entry\r
252                 }\r
253         }\r
254 }\r
255 \r
256 void CHistoryCombo::SetURLHistory(BOOL bURLHistory)\r
257 {\r
258         m_bURLHistory = bURLHistory;\r
259 \r
260         if (m_bURLHistory)\r
261         {\r
262                 HWND hwndEdit;\r
263                 // use for ComboEx\r
264                 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);\r
265                 if (NULL == hwndEdit)\r
266                 {\r
267                         // Try the unofficial way of getting the edit control CWnd*\r
268                         CWnd* pWnd = this->GetDlgItem(1001);\r
269                         if(pWnd)\r
270                         {\r
271                                 hwndEdit = pWnd->GetSafeHwnd();\r
272                         }\r
273                 }\r
274                 if (hwndEdit)\r
275                         SHAutoComplete(hwndEdit, SHACF_URLALL);\r
276         }\r
277 \r
278 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
279         SetImageList(&SYS_IMAGE_LIST());\r
280 #endif\r
281 }\r
282 \r
283 void CHistoryCombo::SetPathHistory(BOOL bPathHistory)\r
284 {\r
285         m_bPathHistory = bPathHistory;\r
286 \r
287         if (m_bPathHistory)\r
288         {\r
289                 HWND hwndEdit;\r
290                 // use for ComboEx\r
291                 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);\r
292                 if (NULL == hwndEdit)\r
293                 {\r
294                         //if not, try the old standby\r
295                         if(hwndEdit==NULL)\r
296                         {\r
297                                 CWnd* pWnd = this->GetDlgItem(1001);\r
298                                 if(pWnd)\r
299                                 {\r
300                                         hwndEdit = pWnd->GetSafeHwnd();\r
301                                 }\r
302                         }\r
303                 }\r
304                 if (hwndEdit)\r
305                         SHAutoComplete(hwndEdit, SHACF_FILESYSTEM);\r
306         }\r
307 \r
308 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
309         SetImageList(&SYS_IMAGE_LIST());\r
310 #endif\r
311 }\r
312 \r
313 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems)\r
314 {\r
315         m_nMaxHistoryItems = nMaxItems;\r
316 \r
317         //truncate list to nMaxItems\r
318         int nNumItems = GetCount();\r
319         for (int n = m_nMaxHistoryItems; n < nNumItems; n++)\r
320                 DeleteString(m_nMaxHistoryItems);\r
321 }\r
322 void CHistoryCombo::AddString(STRING_VECTOR &list)\r
323 {\r
324         for(unsigned int i=0;i<list.size();i++)\r
325         {\r
326                 AddString(list[i]);\r
327         }\r
328 }\r
329 CString CHistoryCombo::GetString() const\r
330 {\r
331         CString str;\r
332         int sel;\r
333         sel = GetCurSel();\r
334         DWORD style=GetStyle();\r
335         \r
336         if (sel == CB_ERR)\r
337         {\r
338                 GetWindowText(str);\r
339                 return str;\r
340         }\r
341 \r
342         if ((m_bURLHistory)||(m_bPathHistory) || (!(style&CBS_SIMPLE)) )\r
343         {\r
344                 //URL and path history combo boxes are editable, so get\r
345                 //the string directly from the combobox\r
346                 GetLBText(sel, str.GetBuffer(GetLBTextLen(sel)));\r
347                 str.ReleaseBuffer();\r
348                 return str;\r
349         }\r
350         return m_arEntries.GetAt(sel);\r
351 }\r
352 \r
353 BOOL CHistoryCombo::RemoveSelectedItem()\r
354 {\r
355         int nIndex = GetCurSel();\r
356         if (nIndex == CB_ERR)\r
357         {\r
358                 return FALSE;\r
359         }\r
360 \r
361         DeleteItem(nIndex);\r
362         m_arEntries.RemoveAt(nIndex);\r
363 \r
364         if ( nIndex < GetCount() )\r
365         {\r
366                 // index stays the same to select the\r
367                 // next item after the item which has\r
368                 // just been deleted\r
369         }\r
370         else\r
371         {\r
372                 // the end of the list has been reached\r
373                 // so we select the previous item\r
374                 nIndex--;\r
375         }\r
376 \r
377         if ( nIndex == -1 )\r
378         {\r
379                 // The one and only item has just been\r
380                 // deleted -> reset window text since\r
381                 // there is no item to select\r
382                 SetWindowText(_T(""));\r
383         }\r
384         else\r
385         {\r
386                 SetCurSel(nIndex);\r
387         }\r
388 \r
389         // Since the dialog might be canceled we\r
390         // should now save the history. Before that\r
391         // set the selection to the first item so that\r
392         // the items will not be reordered and restore\r
393         // the selection after saving.\r
394         SetCurSel(0);\r
395         SaveHistory();\r
396         if ( nIndex != -1 )\r
397         {\r
398                 SetCurSel(nIndex);\r
399         }\r
400 \r
401         return TRUE;\r
402 }\r
403 \r
404 void CHistoryCombo::PreSubclassWindow()\r
405 {\r
406         CComboBoxEx::PreSubclassWindow();\r
407 \r
408         if (!m_bDyn)\r
409                 CreateToolTip();\r
410 }\r
411 \r
412 void CHistoryCombo::OnMouseMove(UINT nFlags, CPoint point)\r
413 {\r
414         CRect rectClient;\r
415         GetClientRect(&rectClient);\r
416         int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;\r
417         rectClient.right = rectClient.right - nComboButtonWidth;\r
418 \r
419         if (rectClient.PtInRect(point))\r
420         {\r
421                 ClientToScreen(&rectClient);\r
422 \r
423                 CString strText = GetString();\r
424                 m_ToolInfo.lpszText = (LPTSTR)(LPCTSTR)strText;\r
425 \r
426                 HDC hDC = ::GetDC(m_hWnd);\r
427 \r
428                 CFont *pFont = GetFont();\r
429                 HFONT hOldFont = (HFONT) ::SelectObject(hDC, (HFONT) *pFont);\r
430 \r
431                 SIZE size;\r
432                 ::GetTextExtentPoint32(hDC, strText, strText.GetLength(), &size);\r
433                 ::SelectObject(hDC, hOldFont);\r
434                 ::ReleaseDC(m_hWnd, hDC);\r
435 \r
436                 if (size.cx > (rectClient.Width() - 6))\r
437                 {\r
438                         rectClient.left += 1;\r
439                         rectClient.top += 3;\r
440 \r
441                         COLORREF rgbText = ::GetSysColor(COLOR_WINDOWTEXT);\r
442                         COLORREF rgbBackground = ::GetSysColor(COLOR_WINDOW);\r
443 \r
444                         CWnd *pWnd = GetFocus();\r
445                         if (pWnd)\r
446                         {\r
447                                 if (pWnd->m_hWnd == m_hWnd)\r
448                                 {\r
449                                         rgbText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);\r
450                                         rgbBackground = ::GetSysColor(COLOR_HIGHLIGHT);\r
451                                 }\r
452                         }\r
453 \r
454                         if (!m_ttShown)\r
455                         {\r
456                                 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, rgbBackground, 0);\r
457                                 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, rgbText, 0);\r
458                                 ::SendMessage(m_hWndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM) &m_ToolInfo);\r
459                                 ::SendMessage(m_hWndToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rectClient.left, rectClient.top));\r
460                                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
461                                 SetTimer(1, 80, NULL);\r
462                                 SetTimer(2, 2000, NULL);\r
463                                 m_ttShown = TRUE;\r
464                         }\r
465                 }\r
466                 else\r
467                 {\r
468                         ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
469                         m_ttShown = FALSE;\r
470                 }\r
471         }\r
472         else\r
473         {\r
474                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
475                 m_ttShown = FALSE;\r
476         }\r
477 \r
478         CComboBoxEx::OnMouseMove(nFlags, point);\r
479 }\r
480 \r
481 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent)\r
482 {\r
483         CPoint point;\r
484         DWORD ptW = GetMessagePos();\r
485         point.x = GET_X_LPARAM(ptW);\r
486         point.y = GET_Y_LPARAM(ptW);\r
487         ScreenToClient(&point);\r
488 \r
489         CRect rectClient;\r
490         GetClientRect(&rectClient);\r
491         int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;\r
492 \r
493         rectClient.right = rectClient.right - nComboButtonWidth;\r
494 \r
495         if (!rectClient.PtInRect(point))\r
496         {\r
497                 KillTimer(nIDEvent);\r
498                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
499                 m_ttShown = FALSE;\r
500         }\r
501         if (nIDEvent == 2)\r
502         {\r
503                 // tooltip timeout, just deactivate it\r
504                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
505                 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again\r
506                 // without the mouse pointer first leaving the control and entering it again\r
507         }\r
508 \r
509         CComboBoxEx::OnTimer(nIDEvent);\r
510 }\r
511 \r
512 void CHistoryCombo::CreateToolTip()\r
513 {\r
514         // create tooltip\r
515         m_hWndToolTip = ::CreateWindowEx(WS_EX_TOPMOST,\r
516                 TOOLTIPS_CLASS,\r
517                 NULL,\r
518                 TTS_NOPREFIX | TTS_ALWAYSTIP,\r
519                 CW_USEDEFAULT,\r
520                 CW_USEDEFAULT,\r
521                 CW_USEDEFAULT,\r
522                 CW_USEDEFAULT,\r
523                 m_hWnd,\r
524                 NULL,\r
525                 NULL,\r
526                 NULL);\r
527 \r
528         // initialize tool info struct\r
529         memset(&m_ToolInfo, 0, sizeof(m_ToolInfo));\r
530         m_ToolInfo.cbSize = sizeof(m_ToolInfo);\r
531         m_ToolInfo.uFlags = TTF_TRANSPARENT;\r
532         m_ToolInfo.hwnd = m_hWnd;\r
533 \r
534         ::SendMessage(m_hWndToolTip, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);\r
535         ::SendMessage(m_hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);\r
536         ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, ::GetSysColor(COLOR_HIGHLIGHT), 0);\r
537         ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, ::GetSysColor(COLOR_HIGHLIGHTTEXT), 0);\r
538 \r
539         CRect rectMargins(0,-1,0,-1);\r
540         ::SendMessage(m_hWndToolTip, TTM_SETMARGIN, 0, (LPARAM)&rectMargins);\r
541 \r
542         CFont *pFont = GetFont();\r
543         ::SendMessage(m_hWndToolTip, WM_SETFONT, (WPARAM)(HFONT)*pFont, FALSE);\r
544 }\r
545 \r
546 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct)\r
547 {\r
548         if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)\r
549                 return -1;\r
550 \r
551         if (m_bDyn)\r
552                 CreateToolTip();\r
553 \r
554         return 0;\r
555 }\r
556 \r