OSDN Git Service

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