OSDN Git Service

ce82cd1a78ef9ef6c35f91274febd6151a539be7
[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         }\r
156 \r
157         //truncate list to m_nMaxHistoryItems\r
158         int nNumItems = GetCount();\r
159         for (int n = m_nMaxHistoryItems; n < nNumItems; n++)\r
160         {\r
161                 DeleteItem(m_nMaxHistoryItems);\r
162                 m_arEntries.RemoveAt(m_nMaxHistoryItems);\r
163         }\r
164 \r
165         SetCurSel(nRet);\r
166         return nRet;\r
167 }\r
168 \r
169 CString CHistoryCombo::LoadHistory(LPCTSTR lpszSection, LPCTSTR lpszKeyPrefix)\r
170 {\r
171         if (lpszSection == NULL || lpszKeyPrefix == NULL || *lpszSection == '\0')\r
172                 return _T("");\r
173 \r
174         m_sSection = lpszSection;\r
175         m_sKeyPrefix = lpszKeyPrefix;\r
176 \r
177         int n = 0;\r
178         CString sText;\r
179         do\r
180         {\r
181                 //keys are of form <lpszKeyPrefix><entrynumber>\r
182                 CString sKey;\r
183                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n++);\r
184                 sText = CRegString(sKey);\r
185                 if (!sText.IsEmpty())\r
186                         AddString(sText);\r
187         } while (!sText.IsEmpty() && n < m_nMaxHistoryItems);\r
188 \r
189         SetCurSel(-1);\r
190 \r
191         ModifyStyleEx(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 0);\r
192 \r
193         // need to resize the control for correct display\r
194         CRect rect;\r
195         GetWindowRect(rect);\r
196         GetParent()->ScreenToClient(rect);\r
197         MoveWindow(rect.left, rect.top, rect.Width(),100);\r
198 \r
199         return sText;\r
200 }\r
201 \r
202 void CHistoryCombo::SaveHistory()\r
203 {\r
204         if (m_sSection.IsEmpty())\r
205                 return;\r
206 \r
207         //add the current item to the history\r
208         CString sCurItem;\r
209         GetWindowText(sCurItem);\r
210         sCurItem.Trim();\r
211         if (!sCurItem.IsEmpty())\r
212                 AddString(sCurItem, 0);\r
213         //save history to registry/inifile\r
214         int nMax = min(GetCount(), m_nMaxHistoryItems + 1);\r
215         for (int n = 0; n < nMax; n++)\r
216         {\r
217                 CString sKey;\r
218                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
219                 CRegString regkey = CRegString(sKey);\r
220                 regkey = m_arEntries.GetAt(n);\r
221         }\r
222         //remove items exceeding the max number of history items\r
223         for (int n = nMax; ; n++)\r
224         {\r
225                 CString sKey;\r
226                 sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
227                 CRegString regkey = CRegString(sKey);\r
228                 CString sText = regkey;\r
229                 if (sText.IsEmpty())\r
230                         break;\r
231                 regkey.removeValue(); // remove entry\r
232         }\r
233 }\r
234 \r
235 void CHistoryCombo::ClearHistory(BOOL bDeleteRegistryEntries/*=TRUE*/)\r
236 {\r
237         ResetContent();\r
238         if (! m_sSection.IsEmpty() && bDeleteRegistryEntries)\r
239         {\r
240                 //remove profile entries\r
241                 CString sKey;\r
242                 for (int n = 0; ; n++)\r
243                 {\r
244                         sKey.Format(_T("%s\\%s%d"), (LPCTSTR)m_sSection, (LPCTSTR)m_sKeyPrefix, n);\r
245                         CRegString regkey = CRegString(sKey);\r
246                         CString sText = regkey;\r
247                         if (sText.IsEmpty())\r
248                                 break;\r
249                         regkey.removeValue(); // remove entry\r
250                 }\r
251         }\r
252 }\r
253 \r
254 void CHistoryCombo::SetURLHistory(BOOL bURLHistory)\r
255 {\r
256         m_bURLHistory = bURLHistory;\r
257 \r
258         if (m_bURLHistory)\r
259         {\r
260                 HWND hwndEdit;\r
261                 // use for ComboEx\r
262                 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);\r
263                 if (NULL == hwndEdit)\r
264                 {\r
265                         // Try the unofficial way of getting the edit control CWnd*\r
266                         CWnd* pWnd = this->GetDlgItem(1001);\r
267                         if(pWnd)\r
268                         {\r
269                                 hwndEdit = pWnd->GetSafeHwnd();\r
270                         }\r
271                 }\r
272                 if (hwndEdit)\r
273                         SHAutoComplete(hwndEdit, SHACF_URLALL);\r
274         }\r
275 \r
276 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
277         SetImageList(&SYS_IMAGE_LIST());\r
278 #endif\r
279 }\r
280 \r
281 void CHistoryCombo::SetPathHistory(BOOL bPathHistory)\r
282 {\r
283         m_bPathHistory = bPathHistory;\r
284 \r
285         if (m_bPathHistory)\r
286         {\r
287                 HWND hwndEdit;\r
288                 // use for ComboEx\r
289                 hwndEdit = (HWND)::SendMessage(this->m_hWnd, CBEM_GETEDITCONTROL, 0, 0);\r
290                 if (NULL == hwndEdit)\r
291                 {\r
292                         //if not, try the old standby\r
293                         if(hwndEdit==NULL)\r
294                         {\r
295                                 CWnd* pWnd = this->GetDlgItem(1001);\r
296                                 if(pWnd)\r
297                                 {\r
298                                         hwndEdit = pWnd->GetSafeHwnd();\r
299                                 }\r
300                         }\r
301                 }\r
302                 if (hwndEdit)\r
303                         SHAutoComplete(hwndEdit, SHACF_FILESYSTEM);\r
304         }\r
305 \r
306 #ifdef HISTORYCOMBO_WITH_SYSIMAGELIST\r
307         SetImageList(&SYS_IMAGE_LIST());\r
308 #endif\r
309 }\r
310 \r
311 void CHistoryCombo::SetMaxHistoryItems(int nMaxItems)\r
312 {\r
313         m_nMaxHistoryItems = nMaxItems;\r
314 \r
315         //truncate list to nMaxItems\r
316         int nNumItems = GetCount();\r
317         for (int n = m_nMaxHistoryItems; n < nNumItems; n++)\r
318                 DeleteString(m_nMaxHistoryItems);\r
319 }\r
320 void CHistoryCombo::AddString(STRING_VECTOR &list)\r
321 {\r
322         for(unsigned int i=0;i<list.size();i++)\r
323         {\r
324                 AddString(list[i]);\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         DWORD style=GetStyle();\r
333         \r
334         if (sel == CB_ERR)\r
335         {\r
336                 GetWindowText(str);\r
337                 return str;\r
338         }\r
339 \r
340         if ((m_bURLHistory)||(m_bPathHistory) || (!(style&CBS_SIMPLE)) )\r
341         {\r
342                 //URL and path history combo boxes are editable, so get\r
343                 //the string directly from the combobox\r
344                 GetLBText(sel, str.GetBuffer(GetLBTextLen(sel)));\r
345                 str.ReleaseBuffer();\r
346                 return str;\r
347         }\r
348         return m_arEntries.GetAt(sel);\r
349 }\r
350 \r
351 BOOL CHistoryCombo::RemoveSelectedItem()\r
352 {\r
353         int nIndex = GetCurSel();\r
354         if (nIndex == CB_ERR)\r
355         {\r
356                 return FALSE;\r
357         }\r
358 \r
359         DeleteItem(nIndex);\r
360         m_arEntries.RemoveAt(nIndex);\r
361 \r
362         if ( nIndex < GetCount() )\r
363         {\r
364                 // index stays the same to select the\r
365                 // next item after the item which has\r
366                 // just been deleted\r
367         }\r
368         else\r
369         {\r
370                 // the end of the list has been reached\r
371                 // so we select the previous item\r
372                 nIndex--;\r
373         }\r
374 \r
375         if ( nIndex == -1 )\r
376         {\r
377                 // The one and only item has just been\r
378                 // deleted -> reset window text since\r
379                 // there is no item to select\r
380                 SetWindowText(_T(""));\r
381         }\r
382         else\r
383         {\r
384                 SetCurSel(nIndex);\r
385         }\r
386 \r
387         // Since the dialog might be canceled we\r
388         // should now save the history. Before that\r
389         // set the selection to the first item so that\r
390         // the items will not be reordered and restore\r
391         // the selection after saving.\r
392         SetCurSel(0);\r
393         SaveHistory();\r
394         if ( nIndex != -1 )\r
395         {\r
396                 SetCurSel(nIndex);\r
397         }\r
398 \r
399         return TRUE;\r
400 }\r
401 \r
402 void CHistoryCombo::PreSubclassWindow()\r
403 {\r
404         CComboBoxEx::PreSubclassWindow();\r
405 \r
406         if (!m_bDyn)\r
407                 CreateToolTip();\r
408 }\r
409 \r
410 void CHistoryCombo::OnMouseMove(UINT nFlags, CPoint point)\r
411 {\r
412         CRect rectClient;\r
413         GetClientRect(&rectClient);\r
414         int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;\r
415         rectClient.right = rectClient.right - nComboButtonWidth;\r
416 \r
417         if (rectClient.PtInRect(point))\r
418         {\r
419                 ClientToScreen(&rectClient);\r
420 \r
421                 CString strText = GetString();\r
422                 m_ToolInfo.lpszText = (LPTSTR)(LPCTSTR)strText;\r
423 \r
424                 HDC hDC = ::GetDC(m_hWnd);\r
425 \r
426                 CFont *pFont = GetFont();\r
427                 HFONT hOldFont = (HFONT) ::SelectObject(hDC, (HFONT) *pFont);\r
428 \r
429                 SIZE size;\r
430                 ::GetTextExtentPoint32(hDC, strText, strText.GetLength(), &size);\r
431                 ::SelectObject(hDC, hOldFont);\r
432                 ::ReleaseDC(m_hWnd, hDC);\r
433 \r
434                 if (size.cx > (rectClient.Width() - 6))\r
435                 {\r
436                         rectClient.left += 1;\r
437                         rectClient.top += 3;\r
438 \r
439                         COLORREF rgbText = ::GetSysColor(COLOR_WINDOWTEXT);\r
440                         COLORREF rgbBackground = ::GetSysColor(COLOR_WINDOW);\r
441 \r
442                         CWnd *pWnd = GetFocus();\r
443                         if (pWnd)\r
444                         {\r
445                                 if (pWnd->m_hWnd == m_hWnd)\r
446                                 {\r
447                                         rgbText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);\r
448                                         rgbBackground = ::GetSysColor(COLOR_HIGHLIGHT);\r
449                                 }\r
450                         }\r
451 \r
452                         if (!m_ttShown)\r
453                         {\r
454                                 ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, rgbBackground, 0);\r
455                                 ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, rgbText, 0);\r
456                                 ::SendMessage(m_hWndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM) &m_ToolInfo);\r
457                                 ::SendMessage(m_hWndToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rectClient.left, rectClient.top));\r
458                                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
459                                 SetTimer(1, 80, NULL);\r
460                                 SetTimer(2, 2000, NULL);\r
461                                 m_ttShown = TRUE;\r
462                         }\r
463                 }\r
464                 else\r
465                 {\r
466                         ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
467                         m_ttShown = FALSE;\r
468                 }\r
469         }\r
470         else\r
471         {\r
472                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
473                 m_ttShown = FALSE;\r
474         }\r
475 \r
476         CComboBoxEx::OnMouseMove(nFlags, point);\r
477 }\r
478 \r
479 void CHistoryCombo::OnTimer(UINT_PTR nIDEvent)\r
480 {\r
481         CPoint point;\r
482         DWORD ptW = GetMessagePos();\r
483         point.x = GET_X_LPARAM(ptW);\r
484         point.y = GET_Y_LPARAM(ptW);\r
485         ScreenToClient(&point);\r
486 \r
487         CRect rectClient;\r
488         GetClientRect(&rectClient);\r
489         int nComboButtonWidth = ::GetSystemMetrics(SM_CXHTHUMB) + 2;\r
490 \r
491         rectClient.right = rectClient.right - nComboButtonWidth;\r
492 \r
493         if (!rectClient.PtInRect(point))\r
494         {\r
495                 KillTimer(nIDEvent);\r
496                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
497                 m_ttShown = FALSE;\r
498         }\r
499         if (nIDEvent == 2)\r
500         {\r
501                 // tooltip timeout, just deactivate it\r
502                 ::SendMessage(m_hWndToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)(LPTOOLINFO) &m_ToolInfo);\r
503                 // don't set m_ttShown to FALSE, because we don't want the tooltip to show up again\r
504                 // without the mouse pointer first leaving the control and entering it again\r
505         }\r
506 \r
507         CComboBoxEx::OnTimer(nIDEvent);\r
508 }\r
509 \r
510 void CHistoryCombo::CreateToolTip()\r
511 {\r
512         // create tooltip\r
513         m_hWndToolTip = ::CreateWindowEx(WS_EX_TOPMOST,\r
514                 TOOLTIPS_CLASS,\r
515                 NULL,\r
516                 TTS_NOPREFIX | TTS_ALWAYSTIP,\r
517                 CW_USEDEFAULT,\r
518                 CW_USEDEFAULT,\r
519                 CW_USEDEFAULT,\r
520                 CW_USEDEFAULT,\r
521                 m_hWnd,\r
522                 NULL,\r
523                 NULL,\r
524                 NULL);\r
525 \r
526         // initialize tool info struct\r
527         memset(&m_ToolInfo, 0, sizeof(m_ToolInfo));\r
528         m_ToolInfo.cbSize = sizeof(m_ToolInfo);\r
529         m_ToolInfo.uFlags = TTF_TRANSPARENT;\r
530         m_ToolInfo.hwnd = m_hWnd;\r
531 \r
532         ::SendMessage(m_hWndToolTip, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);\r
533         ::SendMessage(m_hWndToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_ToolInfo);\r
534         ::SendMessage(m_hWndToolTip, TTM_SETTIPBKCOLOR, ::GetSysColor(COLOR_HIGHLIGHT), 0);\r
535         ::SendMessage(m_hWndToolTip, TTM_SETTIPTEXTCOLOR, ::GetSysColor(COLOR_HIGHLIGHTTEXT), 0);\r
536 \r
537         CRect rectMargins(0,-1,0,-1);\r
538         ::SendMessage(m_hWndToolTip, TTM_SETMARGIN, 0, (LPARAM)&rectMargins);\r
539 \r
540         CFont *pFont = GetFont();\r
541         ::SendMessage(m_hWndToolTip, WM_SETFONT, (WPARAM)(HFONT)*pFont, FALSE);\r
542 }\r
543 \r
544 int CHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct)\r
545 {\r
546         if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)\r
547                 return -1;\r
548 \r
549         if (m_bDyn)\r
550                 CreateToolTip();\r
551 \r
552         return 0;\r
553 }\r
554 \r