OSDN Git Service

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