1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (c) 2003 by Andreas Kapust <info@akinstaller.de>; <http://www.codeproject.com/Articles/2607/AutoComplete-without-IAutoComplete>
4 // Copyright (C) 2009, 2012-2013, 2015-2016, 2018 - TortoiseGit
6 // Licensed under: The Code Project Open License (CPOL); <http://www.codeproject.com/info/cpol10.aspx>
8 // ACWnd.cpp: Implementierungsdatei
12 #include "ACListWnd.h"
13 #include "StringUtils.h"
18 static char THIS_FILE
[] = __FILE__
;
21 static UINT auIDStatusBar
[] =
26 #define _MAX_ENTRYS_ 8
27 #define _MODE_FIND_ALL_ (1L << 5)
29 /////////////////////////////////////////////////////////////////////////////
32 void DoPaintMessageLoop()
35 while (::PeekMessage(&message1
, nullptr, WM_PAINT
, WM_PAINT
, PM_REMOVE
))
37 ::TranslateMessage(&message1
);
38 ::DispatchMessage(&message1
);
42 /**********************************************************************/
44 CACListWnd::CACListWnd()
51 m_pEditParent
= nullptr;
52 m_LastSize
.SetRectEmpty();
57 SecureZeroMemory(&logfont
, sizeof(LOGFONT
));
59 NONCLIENTMETRICS metrics
= { 0 };
60 metrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
61 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, 0, &metrics
, FALSE
);
62 m_uiFont
.CreateFontIndirect(&metrics
.lfMessageFont
);
65 /**********************************************************************/
67 CACListWnd::~CACListWnd()
69 m_SearchList
.RemoveAll();
70 m_DisplayList
.RemoveAll();
74 /*********************************************************************/
76 void CACListWnd::OnActivateApp(BOOL bActive
, DWORD dwThreadID
)
78 #if (_MSC_VER >= 1300)
79 CWnd::OnActivateApp(bActive
, dwThreadID
); //vc7 FIX 1.2
81 CWnd::OnActivateApp(bActive
, (HTASK
)dwThreadID
); //vc6 FIX 1.2
88 BEGIN_MESSAGE_MAP(CACListWnd
, CWnd
)
89 //{{AFX_MSG_MAP(CACListWnd)
103 ON_WM_NCLBUTTONDOWN()
106 ON_WM_GETMINMAXINFO()
111 /////////////////////////////////////////////////////////////////////////////
112 // Behandlungsroutinen für Nachrichten CACListWnd
114 void CACListWnd::DrawItem(CDC
* pDC
,long m_lItem
,long width
)
116 long y
= m_lItem
- m_lTopIndex
;
117 CRect
rcLabel(2,y
*m_ItemHeight
,width
,(y
+1)*m_ItemHeight
);
119 pDC
->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT
));
121 if(m_lItem
== m_lSelItem
)
124 pDC
->FillSolidRect(rcLabel
,::GetSysColor(COLOR_HIGHLIGHT
));
125 pDC
->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT
));
130 m_DisplayStr
= m_PrefixChar
+ m_DisplayList
.GetAt(m_lItem
);
132 m_DisplayStr
= m_DisplayList
.GetAt(m_lItem
);
134 pDC
->DrawText(m_DisplayStr
, -1, rcLabel
, DT_LEFT
| DT_SINGLELINE
|
135 DT_NOPREFIX
| DT_VCENTER
| DT_END_ELLIPSIS
);
138 /*********************************************************************/
140 void CACListWnd::OnPaint()
143 CRect rcWnd
,m_rect
, rc
;
144 CDC MemDC
, *pDC
= nullptr;
145 CBitmap m_bitmap
, *m_pOldBitmap
;
151 rc
.left
= rc
.right
-GetSystemMetrics(SM_CXHSCROLL
);
152 rc
.top
= rc
.bottom
-GetSystemMetrics(SM_CYVSCROLL
);
154 m_rect
.right
-= ScrollBarWidth();
156 MemDC
.CreateCompatibleDC(&dc
);
158 m_bitmap
.CreateCompatibleBitmap(&dc
, m_rect
.Width(), m_rect
.Height());
159 m_pOldBitmap
= MemDC
.SelectObject(&m_bitmap
);
161 MemDC
.SetWindowOrg(m_rect
.left
, m_rect
.top
);
163 long width
= rcWnd
.Width() - ScrollBarWidth();
165 MemDC
.FillSolidRect(rcWnd
,::GetSysColor(COLOR_WINDOW
));
166 MemDC
.SelectObject(m_uiFont
);
167 MemDC
.SetBkMode(TRANSPARENT
);
169 for(i
= m_lTopIndex
; i
< m_lCount
;i
++)
171 DrawItem(&MemDC
,i
,width
);
175 CPen
m_Pen1(PS_SOLID
, 1, ::GetSysColor(COLOR_WINDOW
));
176 CPen
m_Pen2(PS_SOLID
, 1, ::GetSysColor(COLOR_BTNFACE
));
177 CPen
m_Pen3(PS_SOLID
, 1, ::GetSysColor(COLOR_3DSHADOW
));
181 if(m_VertBar
.IsWindowVisible())
182 dc
.FillSolidRect(rc
, ::GetSysColor(COLOR_BTNFACE
) );
186 CPen
*pOldPen
= (CPen
*)pDC
->SelectObject(&m_Pen1
);
189 width
= GetSystemMetrics(SM_CXHSCROLL
);
190 bottom
= (rcWnd
.bottom
-GetSystemMetrics(SM_CXHSCROLL
))-1;
193 for( i
= 0; i
< 20 ;i
++,a
++)
196 pDC
->SelectObject(&m_Pen1
);
198 pDC
->SelectObject(&m_Pen2
);
200 pDC
->SelectObject(&m_Pen3
);
204 pDC
->MoveTo(rc
.left
+ i
- 1, rcWnd
.bottom
);
205 pDC
->LineTo(rc
.left
+ i
+ width
, bottom
);
208 dc
.BitBlt(m_rect
.left
, m_rect
.top
, m_rect
.Width(), m_rect
.Height(),
209 &MemDC
, m_rect
.left
, m_rect
.top
, SRCCOPY
);
211 pDC
->SelectObject( pOldPen
);
212 MemDC
.SelectObject(m_pOldBitmap
);
215 /*********************************************************************/
217 void CACListWnd::Init(CWnd
*pWnd
)
219 VERIFY(m_VertBar
.Create(WS_VISIBLE
|SBS_VERT
|SBS_LEFTALIGN
,
220 CRect(0,0,GetSystemMetrics(SM_CYVSCROLL
),100),this,0));
223 m_pEditParent
= (CEdit
*)pWnd
;
225 m_lCount
= (long)m_DisplayList
.GetSize();
226 m_VertBar
.SetScrollPos(0,false);
233 m_pDC
->SelectObject(GetStockObject(DEFAULT_GUI_FONT
));
234 CSize m_Size
= m_pDC
->GetOutputTextExtent(L
"Hg");
235 m_ItemHeight
= m_Size
.cy
;
240 /*********************************************************************/
242 void CACListWnd::SetScroller()
245 GetClientRect(rcWnd
);
247 if(m_VertBar
.GetSafeHwnd())
251 rcBar
.left
= (rcWnd
.Width()-GetSystemMetrics(SM_CYVSCROLL
));
252 rcBar
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
253 m_VertBar
.MoveWindow(rcBar
);
254 rcBar
.top
= rcWnd
.bottom
-20;
255 rcBar
.bottom
= rcWnd
.bottom
;
257 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
261 /*********************************************************************/
263 void CACListWnd::OnSize(UINT nType
, int cx
, int cy
)
265 CWnd::OnSize(nType
, cx
, cy
);
269 if(!m_LastSize
.IsRectEmpty())
270 GetWindowRect(m_LastSize
);
273 /*********************************************************************/
275 long CACListWnd::ScrollBarWidth()
277 if(m_VertBar
.IsWindowVisible())
278 return GetSystemMetrics(SM_CYVSCROLL
);
283 /*********************************************************************/
285 void CACListWnd::SetProp()
292 CWnd::GetWindowRect(rcWnd
);
293 ScreenToClient(rcWnd
);
296 si
.cbSize
= sizeof(SCROLLINFO
);
297 si
.fMask
= SIF_PAGE
|SIF_RANGE
;
299 si
.nMax
= m_lCount
-1;
300 m_VisibleItems
= si
.nPage
= rcWnd
.Height()/m_ItemHeight
;
302 m_VertBar
.SetScrollRange(0,m_lCount
-1);
303 m_VertBar
.SetScrollInfo(&si
);
305 if(m_VisibleItems
> m_lCount
-1)
306 m_VertBar
.ShowWindow(false);
308 m_VertBar
.ShowWindow(true);
310 if(m_lTopIndex
+m_VisibleItems
> m_lCount
)
312 m_lTopIndex
= m_lCount
-m_VisibleItems
;
315 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
319 /*********************************************************************/
321 BOOL
CACListWnd::OnEraseBkgnd(CDC
* /*pDC*/)
326 /*********************************************************************/
328 void CACListWnd::OnNcPaint()
331 CRect rectClient
, rectWindow
,rcWnd
;
333 GetClientRect(rectClient
);
334 GetWindowRect(rectWindow
);
335 ScreenToClient(rectWindow
);
337 rectClient
.OffsetRect(-(rectWindow
.left
), -(rectWindow
.top
));
338 dc
.ExcludeClipRect(rectClient
);
340 rectWindow
.OffsetRect(-rectWindow
.left
, -rectWindow
.top
);
342 dc
.FillSolidRect(rectWindow
,::GetSysColor(COLOR_WINDOWTEXT
));
345 /*********************************************************************/
347 void CACListWnd::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
349 CWnd::OnKeyDown(nChar
, nRepCnt
, nFlags
);
351 if (nChar
== VK_ESCAPE
)
355 /*********************************************************************/
357 void CACListWnd::OnNcCalcSize(BOOL
/*bCalcValidRects*/, NCCALCSIZE_PARAMS FAR
* lpncsp
)
359 ::InflateRect(lpncsp
->rgrc
,
360 -GetSystemMetrics(SM_CXBORDER
), -GetSystemMetrics(SM_CYBORDER
));
363 /*********************************************************************/
365 int CACListWnd::HitTest(CPoint point
)
370 GetClientRect(rcWnd
);
371 long width
= rcWnd
.Width() - ScrollBarWidth();
373 for(int i
= m_lTopIndex
; i
< m_lCount
; i
++)
375 long y
= i
- m_lTopIndex
;
376 rcItem
.SetRect(2,y
*m_ItemHeight
,width
,(y
+1)*m_ItemHeight
);
378 if(PtInRect(&rcItem
, point
))
379 return (m_lSelItem
= (y
+m_lTopIndex
));
385 /*********************************************************************/
387 LRESULT
CACListWnd::OnNcHitTest(CPoint point
)
390 GetWindowRect(rectClient
);
392 rectClient
.left
= rectClient
.right
- GetSystemMetrics(SM_CYVSCROLL
);
393 rectClient
.top
= rectClient
.bottom
- GetSystemMetrics(SM_CXVSCROLL
);
395 if(rectClient
.PtInRect(point
))
396 return HTBOTTOMRIGHT
;
401 /*********************************************************************/
403 void CACListWnd::OnLButtonDown(UINT nFlags
, CPoint point
)
405 CWnd::OnLButtonDown(nFlags
, point
);
406 int sel
= HitTest(point
);
410 if(!EnsureVisible(sel
,true))
413 m_pEditParent
->SendMessage(ENAC_UPDATE
, WM_KEYDOWN
, GetDlgCtrlID());
414 DoPaintMessageLoop();
422 if(!rc
.PtInRect(point
))
427 /*********************************************************************/
429 void CACListWnd::OnRButtonDown(UINT nFlags
, CPoint point
)
431 CWnd::OnRButtonDown(nFlags
, point
);
435 /*********************************************************************/
437 BOOL
CACListWnd::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
442 GetWindowRect(rectClient
);
443 ScreenToClient(&rectClient
);
445 rectClient
.left
= rectClient
.right
- GetSystemMetrics(SM_CYVSCROLL
);
446 rectClient
.top
= rectClient
.bottom
- GetSystemMetrics(SM_CXVSCROLL
);
449 GetCursorPos(&ptCursor
);
450 ScreenToClient(&ptCursor
);
452 if(rectClient
.PtInRect(ptCursor
)) // Vergrößerungs-Cursor
454 return CWnd::OnSetCursor(pWnd
, nHitTest
, message
);
457 ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
461 /*********************************************************************/
463 void CACListWnd::InvalidateAndScroll()
465 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
467 DoPaintMessageLoop();
470 /*********************************************************************/
472 bool CACListWnd::EnsureVisible(int item
, bool m_bWait
)
474 if(item
> m_lTopIndex
&& item
< m_lTopIndex
+ m_VisibleItems
)
475 return false; // ist visible
477 if(item
> m_lTopIndex
) // scroll down
480 for(int i
= m_lTopIndex
; i
< m_len
; i
++)
482 if(i
>= m_lCount
-m_VisibleItems
)
484 if(i
>= m_lCount
-m_VisibleItems
|| i
+ m_VisibleItems
> item
)
493 InvalidateAndScroll();
495 DoPaintMessageLoop();
498 InvalidateAndScroll();
502 if(item
< m_lTopIndex
) // scroll up
504 while(item
< m_lTopIndex
)
515 InvalidateAndScroll();
517 DoPaintMessageLoop();
521 InvalidateAndScroll();
528 /*********************************************************************/
530 bool CACListWnd::SelectItem(int item
)
537 EnsureVisible(m_lSelItem
,false);
544 if(!EnsureVisible(item
,true))
550 /*********************************************************************/
552 int CACListWnd::FindStringExact( int nStartAfter
, LPCTSTR lpszString
)
554 if(nStartAfter
> m_SearchList
.GetSize())
557 for(int i
= nStartAfter
+1; i
< m_SearchList
.GetSize(); i
++)
558 if(m_SearchList
.GetAt(i
).Compare(lpszString
) == 0)
563 /*********************************************************************/
566 * NEW: m_bDisplayOnly
568 int CACListWnd::FindString(int nStartAfter
, LPCTSTR lpszString
, bool m_bDisplayOnly
)
570 long m_AktCount
= (long)m_DisplayList
.GetSize();
574 CString m_Str1
,m_Str2
= lpszString
;
581 if(nStartAfter
> m_SearchList
.GetSize())
593 m_DisplayList
.RemoveAll();
597 for(int i
= nStartAfter
+1; i
< m_SearchList
.GetSize(); i
++)
600 m_Str1
= m_PrefixChar
;
604 m_Str1
+= m_SearchList
.GetAt(i
);
608 if(m_lMode
& _MODE_FIND_ALL_
)
610 if(m_Str1
.Find(m_Str2
) >= 0)
612 m_DisplayList
.Add(m_SearchList
.GetAt(i
));
615 else // _MODE_FIND_EXACT_
617 if (CStringUtils::StartsWith(m_Str1
, m_Str2
))
619 m_DisplayList
.Add(m_SearchList
.GetAt(i
));
624 m_lCount
= (long)m_DisplayList
.GetSize();
631 m_pEditParent
->GetWindowRect(rcWnd
);
639 iHeight
= m_lCount
*m_ItemHeight
+(GetSystemMetrics(SM_CYBORDER
)*2);
641 if(m_lCount
> _MAX_ENTRYS_
)
642 iHeight
= _MAX_ENTRYS_
*m_ItemHeight
+(GetSystemMetrics(SM_CYBORDER
)*2);
644 if(!m_LastSize
.IsRectEmpty())
646 iWight
= m_LastSize
.Width();
647 iHeight
= m_LastSize
.Height();
648 rcWnd
.top
+= rcWnd
.Height();
649 rcWnd
.right
= rcWnd
.left
+iWight
;
650 rcWnd
.bottom
= rcWnd
.top
+iHeight
;
652 SetWindowPos(&CWnd::wndTopMost
, rcWnd
.left
,
659 SetWindowPos(&CWnd::wndTopMost
, rcWnd
.left
,
660 rcWnd
.top
+ rcWnd
.Height(),
665 if(m_AktCount
!= m_DisplayList
.GetSize())
668 SortList(m_DisplayList
);
678 /*********************************************************************/
680 int CACListWnd::SelectString(LPCTSTR lpszString
)
682 int item
= FindString(-1, lpszString
);
687 /*********************************************************************/
689 bool CACListWnd::GetText(int item
, CString
& m_Text
)
691 if(item
< 0 || item
> m_SearchList
.GetSize())
693 m_Text
= m_SearchList
.GetAt(item
);
697 /*********************************************************************/
699 void CACListWnd::OnShowWindow(BOOL bShow
, UINT nStatus
)
703 m_nIDTimer
= (long)SetTimer(IDTimerInstall
, 200, nullptr);
704 m_pEditParent
->GetParent()->GetWindowRect(m_ParentRect
);
709 KillTimer(IDTimerInstall
);
715 CWnd::OnShowWindow(bShow
, nStatus
);
716 ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
719 /*********************************************************************/
721 void CACListWnd::OnNcLButtonDown(UINT nHitTest
, CPoint point
)
723 if(OnNcHitTest(point
) == HTBOTTOMRIGHT
)
724 GetWindowRect(m_LastSize
);
725 CWnd::OnNcLButtonDown(nHitTest
, point
);
728 /*********************************************************************/
730 CString
CACListWnd::GetString()
732 int i
= (int)m_DisplayList
.GetSize();
736 if(i
<= m_lSelItem
|| m_lSelItem
== -1)
741 return m_DisplayList
.GetAt(i
);
744 /*********************************************************************/
746 void CACListWnd::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
748 CWnd::OnVScroll(nSBCode
, nPos
, pScrollBar
);
749 long m_oldlTopIndex
= m_lTopIndex
;
757 m_lTopIndex
-= m_VisibleItems
;
763 m_lTopIndex
+= m_VisibleItems
;
764 if(m_lTopIndex
>= m_lCount
-m_VisibleItems
)
765 m_lTopIndex
= m_lCount
-m_VisibleItems
;
776 if(m_lTopIndex
>= m_lCount
-m_VisibleItems
)
777 m_lTopIndex
= m_lCount
-m_VisibleItems
;
785 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
787 if(m_oldlTopIndex
!= m_lTopIndex
)
791 /*********************************************************************/
793 CString
CACListWnd::GetNextString(int nChar
)
806 m_lSelItem
-= m_VisibleItems
;
812 m_lSelItem
+= m_VisibleItems
;
813 if(m_lSelItem
>= m_lCount
-1)
814 m_lSelItem
= m_lCount
-1;
822 m_lSelItem
= m_lCount
-1;
827 m_lSelItem
= m_lCount
-1;
829 if(m_lSelItem
>= m_lCount
)
832 if(EnsureVisible(m_lSelItem
,(m_lCount
> 50) ? false : true))
833 InvalidateAndScroll();
838 /*********************************************************************/
840 void CACListWnd::OnMouseMove(UINT nFlags
, CPoint point
)
842 CWnd::OnMouseMove(nFlags
, point
);
843 int sel
= HitTest(point
);
850 /*********************************************************************/
852 void CACListWnd::OnTimer(UINT_PTR nIDEvent
)
854 CWnd::OnTimer(nIDEvent
);
857 m_pEditParent
->GetParent()->GetWindowRect(m_ParentRect1
);
858 if(!m_ParentRect1
.EqualRect(m_ParentRect
))
862 /*********************************************************************/
864 void CACListWnd::OnGetMinMaxInfo(MINMAXINFO FAR
* lpMMI
)
869 long m_lMinY1
= GetSystemMetrics(SM_CYHSCROLL
)*2 + GetSystemMetrics(SM_CYSIZEFRAME
) + GetSystemMetrics(SM_CXHTHUMB
),
870 m_lMinY2
= m_lCount
* m_ItemHeight
+ (GetSystemMetrics(SM_CYBORDER
)*2);
872 if(m_VisibleItems
> m_lCount
-1 && m_lMinY2
< m_lMinY1
)
873 lpMMI
->ptMinTrackSize
.y
= m_lMinY2
;
875 lpMMI
->ptMinTrackSize
.y
= m_lMinY1
;
878 lpMMI
->ptMinTrackSize
.x
= GetSystemMetrics(SM_CXHSCROLL
)*4;
885 m_pEditParent
->GetWindowRect (&rc
);
886 lpMMI
->ptMinTrackSize
.x
= rc
.right
- rc
.left
;
890 CWnd::OnGetMinMaxInfo(lpMMI
);
893 /*********************************************************************/
895 int CACListWnd::CompareString(const void* p1
, const void* p2
)
897 return _stricmp( * ( char** ) p1
, * ( char** ) p2
);
900 /*********************************************************************/
902 void CACListWnd::SortList(CStringArray
& list
)
904 int m_Count
= (int)list
.GetSize();
909 CStringArray m_Liste1
;
912 LPCTSTR
* ppSortArray
= new LPCTSTR
[m_Count
+1];
915 for(i
=0; i
< m_Count
; i
++)
917 ppSortArray
[i
] = (LPCTSTR
)m_Liste1
.GetAt(i
);
922 qsort(ppSortArray
, m_Count
, sizeof(LPCTSTR
), CompareString
);
924 for(i
=0; i
< m_Count
; i
++)
926 list
.Add((LPCTSTR
) ppSortArray
[i
]);
928 m_Liste1
.RemoveAll();
929 delete [] ppSortArray
;
933 /*********************************************************************/
938 void CACListWnd::CopyList()
940 m_DisplayList
.Copy(m_SearchList
);
941 m_lCount
= (long)m_DisplayList
.GetSize();
943 FindString(0, L
"", true);
946 /*********************************************************************/