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-2020, 2023 - 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"
20 static char THIS_FILE
[] = __FILE__
;
23 static UINT auIDStatusBar
[] =
28 #define _MAX_ENTRYS_ 8
29 #define _MODE_FIND_ALL_ (1L << 5)
31 /////////////////////////////////////////////////////////////////////////////
34 void DoPaintMessageLoop()
37 while (::PeekMessage(&message1
, nullptr, WM_PAINT
, WM_PAINT
, PM_REMOVE
))
39 ::TranslateMessage(&message1
);
40 ::DispatchMessage(&message1
);
44 /**********************************************************************/
46 CACListWnd::CACListWnd()
48 NONCLIENTMETRICS metrics
= { 0 };
49 metrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
50 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, 0, &metrics
, FALSE
);
51 m_uiFont
.CreateFontIndirect(&metrics
.lfMessageFont
);
54 /**********************************************************************/
56 CACListWnd::~CACListWnd()
58 m_SearchList
.RemoveAll();
59 m_DisplayList
.RemoveAll();
63 /*********************************************************************/
65 void CACListWnd::OnActivateApp(BOOL bActive
, DWORD dwThreadID
)
67 #if (_MSC_VER >= 1300)
68 CWnd::OnActivateApp(bActive
, dwThreadID
); //vc7 FIX 1.2
70 CWnd::OnActivateApp(bActive
, (HTASK
)dwThreadID
); //vc6 FIX 1.2
77 BEGIN_MESSAGE_MAP(CACListWnd
, CWnd
)
78 //{{AFX_MSG_MAP(CACListWnd)
100 /////////////////////////////////////////////////////////////////////////////
101 // Behandlungsroutinen für Nachrichten CACListWnd
103 void CACListWnd::DrawItem(CDC
* pDC
,long m_lItem
,long width
)
105 long y
= m_lItem
- m_lTopIndex
;
106 CRect
rcLabel(2,y
*m_ItemHeight
,width
,(y
+1)*m_ItemHeight
);
108 pDC
->SetTextColor(CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_WINDOWTEXT
)));
110 if(m_lItem
== m_lSelItem
)
113 pDC
->FillSolidRect(rcLabel
,::GetSysColor(COLOR_HIGHLIGHT
));
114 pDC
->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT
));
119 m_DisplayStr
= m_PrefixChar
+ m_DisplayList
.GetAt(m_lItem
);
121 m_DisplayStr
= m_DisplayList
.GetAt(m_lItem
);
123 pDC
->DrawText(m_DisplayStr
, -1, rcLabel
, DT_LEFT
| DT_SINGLELINE
|
124 DT_NOPREFIX
| DT_VCENTER
| DT_END_ELLIPSIS
);
127 /*********************************************************************/
129 void CACListWnd::OnPaint()
132 CRect rcWnd
,m_rect
, rc
;
133 CDC MemDC
, *pDC
= nullptr;
134 CBitmap m_bitmap
, *m_pOldBitmap
;
139 rc
.left
= rc
.right
-GetSystemMetrics(SM_CXHSCROLL
);
140 rc
.top
= rc
.bottom
-GetSystemMetrics(SM_CYVSCROLL
);
142 m_rect
.right
-= ScrollBarWidth();
144 MemDC
.CreateCompatibleDC(&dc
);
146 m_bitmap
.CreateCompatibleBitmap(&dc
, m_rect
.Width(), m_rect
.Height());
147 m_pOldBitmap
= MemDC
.SelectObject(&m_bitmap
);
149 MemDC
.SetWindowOrg(m_rect
.left
, m_rect
.top
);
151 long width
= rcWnd
.Width() - ScrollBarWidth();
153 MemDC
.FillSolidRect(rcWnd
, CTheme::Instance().IsDarkTheme() ? CTheme::darkBkColor
: CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_WINDOW
)));
154 MemDC
.SelectObject(m_uiFont
);
155 MemDC
.SetBkMode(TRANSPARENT
);
157 for (int i
= m_lTopIndex
; i
< m_lCount
; ++i
)
159 DrawItem(&MemDC
,i
,width
);
163 CPen
m_Pen1(PS_SOLID
, 1, CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: ::GetSysColor(COLOR_WINDOW
));
164 CPen
m_Pen2(PS_SOLID
, 1, ::GetSysColor(COLOR_BTNFACE
));
165 CPen
m_Pen3(PS_SOLID
, 1, ::GetSysColor(COLOR_3DSHADOW
));
169 if(m_VertBar
.IsWindowVisible())
170 dc
.FillSolidRect(rc
, ::GetSysColor(COLOR_BTNFACE
) );
174 CPen
* pOldPen
= pDC
->SelectObject(&m_Pen1
);
177 width
= GetSystemMetrics(SM_CXHSCROLL
);
178 bottom
= (rcWnd
.bottom
-GetSystemMetrics(SM_CXHSCROLL
))-1;
181 for (int i
= 0; i
< 20 ; ++i
, ++a
)
184 pDC
->SelectObject(&m_Pen1
);
186 pDC
->SelectObject(&m_Pen2
);
188 pDC
->SelectObject(&m_Pen3
);
192 pDC
->MoveTo(rc
.left
+ i
- 1, rcWnd
.bottom
);
193 pDC
->LineTo(rc
.left
+ i
+ width
, bottom
);
196 dc
.BitBlt(m_rect
.left
, m_rect
.top
, m_rect
.Width(), m_rect
.Height(),
197 &MemDC
, m_rect
.left
, m_rect
.top
, SRCCOPY
);
199 pDC
->SelectObject( pOldPen
);
200 MemDC
.SelectObject(m_pOldBitmap
);
203 /*********************************************************************/
205 void CACListWnd::Init(CWnd
*pWnd
)
207 VERIFY(m_VertBar
.Create(WS_VISIBLE
|SBS_VERT
|SBS_LEFTALIGN
,
208 CRect(0, 0, GetSystemMetrics(SM_CYVSCROLL
), CDPIAware::Instance().ScaleX(*pWnd
, 100)), this, 0));
211 m_pEditParent
= static_cast<CEdit
*>(pWnd
);
213 m_lCount
= static_cast<long>(m_DisplayList
.GetSize());
214 m_VertBar
.SetScrollPos(0,false);
221 m_pDC
->SelectObject(GetStockObject(DEFAULT_GUI_FONT
));
222 CSize m_Size
= m_pDC
->GetOutputTextExtent(L
"Hg");
223 m_ItemHeight
= m_Size
.cy
;
228 /*********************************************************************/
230 void CACListWnd::SetScroller()
233 GetClientRect(rcWnd
);
235 if(m_VertBar
.GetSafeHwnd())
239 rcBar
.left
= (rcWnd
.Width()-GetSystemMetrics(SM_CYVSCROLL
));
240 rcBar
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
241 m_VertBar
.MoveWindow(rcBar
);
242 rcBar
.top
= rcWnd
.bottom
- CDPIAware::Instance().ScaleY(m_VertBar
.GetSafeHwnd() , 20);
243 rcBar
.bottom
= rcWnd
.bottom
;
245 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
249 /*********************************************************************/
251 void CACListWnd::OnSize(UINT nType
, int cx
, int cy
)
253 CWnd::OnSize(nType
, cx
, cy
);
257 if(!m_LastSize
.IsRectEmpty())
258 GetWindowRect(m_LastSize
);
261 /*********************************************************************/
263 long CACListWnd::ScrollBarWidth()
265 if(m_VertBar
.IsWindowVisible())
266 return GetSystemMetrics(SM_CYVSCROLL
);
271 /*********************************************************************/
273 void CACListWnd::SetProp()
280 CWnd::GetWindowRect(rcWnd
);
281 ScreenToClient(rcWnd
);
284 si
.cbSize
= sizeof(SCROLLINFO
);
285 si
.fMask
= SIF_PAGE
|SIF_RANGE
;
287 si
.nMax
= m_lCount
-1;
288 m_VisibleItems
= si
.nPage
= rcWnd
.Height()/m_ItemHeight
;
290 m_VertBar
.SetScrollRange(0,m_lCount
-1);
291 m_VertBar
.SetScrollInfo(&si
);
293 if(m_VisibleItems
> m_lCount
-1)
294 m_VertBar
.ShowWindow(false);
296 m_VertBar
.ShowWindow(true);
298 if(m_lTopIndex
+m_VisibleItems
> m_lCount
)
300 m_lTopIndex
= m_lCount
-m_VisibleItems
;
303 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
307 /*********************************************************************/
309 BOOL
CACListWnd::OnEraseBkgnd(CDC
* /*pDC*/)
314 /*********************************************************************/
316 void CACListWnd::OnNcPaint()
319 CRect rectClient
, rectWindow
,rcWnd
;
321 GetClientRect(rectClient
);
322 GetWindowRect(rectWindow
);
323 ScreenToClient(rectWindow
);
325 rectClient
.OffsetRect(-(rectWindow
.left
), -(rectWindow
.top
));
326 dc
.ExcludeClipRect(rectClient
);
328 rectWindow
.OffsetRect(-rectWindow
.left
, -rectWindow
.top
);
330 dc
.FillSolidRect(rectWindow
, CTheme::Instance().IsDarkTheme() ? CTheme::darkTextColor
: CTheme::Instance().GetThemeColor(::GetSysColor(COLOR_WINDOWTEXT
)));
333 /*********************************************************************/
335 void CACListWnd::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
337 CWnd::OnKeyDown(nChar
, nRepCnt
, nFlags
);
339 if (nChar
== VK_ESCAPE
)
343 /*********************************************************************/
345 void CACListWnd::OnNcCalcSize(BOOL
/*bCalcValidRects*/, NCCALCSIZE_PARAMS FAR
* lpncsp
)
347 ::InflateRect(lpncsp
->rgrc
,
348 -GetSystemMetrics(SM_CXBORDER
), -GetSystemMetrics(SM_CYBORDER
));
351 /*********************************************************************/
353 int CACListWnd::HitTest(CPoint point
)
358 GetClientRect(rcWnd
);
359 long width
= rcWnd
.Width() - ScrollBarWidth();
361 for(int i
= m_lTopIndex
; i
< m_lCount
; i
++)
363 long y
= i
- m_lTopIndex
;
364 rcItem
.SetRect(2,y
*m_ItemHeight
,width
,(y
+1)*m_ItemHeight
);
366 if(PtInRect(&rcItem
, point
))
367 return (m_lSelItem
= (y
+m_lTopIndex
));
373 /*********************************************************************/
375 LRESULT
CACListWnd::OnNcHitTest(CPoint point
)
378 GetWindowRect(rectClient
);
380 rectClient
.left
= rectClient
.right
- GetSystemMetrics(SM_CYVSCROLL
);
381 rectClient
.top
= rectClient
.bottom
- GetSystemMetrics(SM_CXVSCROLL
);
383 if(rectClient
.PtInRect(point
))
384 return HTBOTTOMRIGHT
;
389 /*********************************************************************/
391 void CACListWnd::OnLButtonDown(UINT nFlags
, CPoint point
)
393 CWnd::OnLButtonDown(nFlags
, point
);
394 int sel
= HitTest(point
);
398 if(!EnsureVisible(sel
,true))
401 m_pEditParent
->SendMessage(ENAC_UPDATE
, WM_KEYDOWN
, GetDlgCtrlID());
402 DoPaintMessageLoop();
410 if(!rc
.PtInRect(point
))
415 /*********************************************************************/
417 void CACListWnd::OnRButtonDown(UINT nFlags
, CPoint point
)
419 CWnd::OnRButtonDown(nFlags
, point
);
423 /*********************************************************************/
425 BOOL
CACListWnd::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
430 GetWindowRect(rectClient
);
431 ScreenToClient(&rectClient
);
433 rectClient
.left
= rectClient
.right
- GetSystemMetrics(SM_CYVSCROLL
);
434 rectClient
.top
= rectClient
.bottom
- GetSystemMetrics(SM_CXVSCROLL
);
437 GetCursorPos(&ptCursor
);
438 ScreenToClient(&ptCursor
);
440 if(rectClient
.PtInRect(ptCursor
)) // Vergrößerungs-Cursor
442 return CWnd::OnSetCursor(pWnd
, nHitTest
, message
);
445 ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
449 /*********************************************************************/
451 void CACListWnd::InvalidateAndScroll()
453 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
455 DoPaintMessageLoop();
458 /*********************************************************************/
460 bool CACListWnd::EnsureVisible(int item
, bool m_bWait
)
462 if(item
> m_lTopIndex
&& item
< m_lTopIndex
+ m_VisibleItems
)
463 return false; // ist visible
465 if(item
> m_lTopIndex
) // scroll down
468 for(int i
= m_lTopIndex
; i
< m_len
; i
++)
470 if(i
>= m_lCount
-m_VisibleItems
)
472 if(i
>= m_lCount
-m_VisibleItems
|| i
+ m_VisibleItems
> item
)
481 InvalidateAndScroll();
483 DoPaintMessageLoop();
486 InvalidateAndScroll();
490 if(item
< m_lTopIndex
) // scroll up
492 while(item
< m_lTopIndex
)
503 InvalidateAndScroll();
505 DoPaintMessageLoop();
509 InvalidateAndScroll();
516 /*********************************************************************/
518 bool CACListWnd::SelectItem(int item
)
525 EnsureVisible(m_lSelItem
,false);
532 if(!EnsureVisible(item
,true))
538 /*********************************************************************/
540 int CACListWnd::FindStringExact( int nStartAfter
, LPCWSTR lpszString
)
542 if(nStartAfter
> m_SearchList
.GetSize())
545 for(int i
= nStartAfter
+1; i
< m_SearchList
.GetSize(); i
++)
546 if(m_SearchList
.GetAt(i
).Compare(lpszString
) == 0)
551 /*********************************************************************/
554 * NEW: m_bDisplayOnly
556 int CACListWnd::FindString(int nStartAfter
, LPCWSTR lpszString
, bool m_bDisplayOnly
)
558 long m_AktCount
= static_cast<long>(m_DisplayList
.GetSize());
562 CString m_Str1
,m_Str2
= lpszString
;
569 if(nStartAfter
> m_SearchList
.GetSize())
581 m_DisplayList
.RemoveAll();
585 for(int i
= nStartAfter
+1; i
< m_SearchList
.GetSize(); i
++)
588 m_Str1
= m_PrefixChar
;
592 m_Str1
+= m_SearchList
.GetAt(i
);
596 if(m_lMode
& _MODE_FIND_ALL_
)
598 if(m_Str1
.Find(m_Str2
) >= 0)
600 m_DisplayList
.Add(m_SearchList
.GetAt(i
));
603 else // _MODE_FIND_EXACT_
605 if (CStringUtils::StartsWith(m_Str1
, m_Str2
))
607 m_DisplayList
.Add(m_SearchList
.GetAt(i
));
612 m_lCount
= static_cast<long>(m_DisplayList
.GetSize());
619 m_pEditParent
->GetWindowRect(rcWnd
);
627 iHeight
= m_lCount
*m_ItemHeight
+(GetSystemMetrics(SM_CYBORDER
)*2);
629 if(m_lCount
> _MAX_ENTRYS_
)
630 iHeight
= _MAX_ENTRYS_
*m_ItemHeight
+(GetSystemMetrics(SM_CYBORDER
)*2);
632 if(!m_LastSize
.IsRectEmpty())
634 iWight
= m_LastSize
.Width();
635 iHeight
= m_LastSize
.Height();
636 rcWnd
.top
+= rcWnd
.Height();
637 rcWnd
.right
= rcWnd
.left
+iWight
;
638 rcWnd
.bottom
= rcWnd
.top
+iHeight
;
640 SetWindowPos(&CWnd::wndTopMost
, rcWnd
.left
,
647 SetWindowPos(&CWnd::wndTopMost
, rcWnd
.left
,
648 rcWnd
.top
+ rcWnd
.Height(),
653 if(m_AktCount
!= m_DisplayList
.GetSize())
656 SortList(m_DisplayList
);
666 /*********************************************************************/
668 int CACListWnd::SelectString(LPCWSTR lpszString
)
670 int item
= FindString(-1, lpszString
);
675 /*********************************************************************/
677 bool CACListWnd::GetText(int item
, CString
& m_Text
)
679 if(item
< 0 || item
> m_SearchList
.GetSize())
681 m_Text
= m_SearchList
.GetAt(item
);
685 /*********************************************************************/
687 void CACListWnd::OnShowWindow(BOOL bShow
, UINT nStatus
)
691 m_nIDTimer
= static_cast<long>(SetTimer(IDTimerInstall
, 200, nullptr));
692 m_pEditParent
->GetParent()->GetWindowRect(m_ParentRect
);
697 KillTimer(IDTimerInstall
);
703 CWnd::OnShowWindow(bShow
, nStatus
);
704 ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
707 /*********************************************************************/
709 void CACListWnd::OnNcLButtonDown(UINT nHitTest
, CPoint point
)
711 if(OnNcHitTest(point
) == HTBOTTOMRIGHT
)
712 GetWindowRect(m_LastSize
);
713 CWnd::OnNcLButtonDown(nHitTest
, point
);
716 /*********************************************************************/
718 CString
CACListWnd::GetString()
720 int i
= static_cast<int>(m_DisplayList
.GetSize());
724 if(i
<= m_lSelItem
|| m_lSelItem
== -1)
729 return m_DisplayList
.GetAt(i
);
732 /*********************************************************************/
734 void CACListWnd::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
736 CWnd::OnVScroll(nSBCode
, nPos
, pScrollBar
);
737 long m_oldlTopIndex
= m_lTopIndex
;
745 m_lTopIndex
-= m_VisibleItems
;
751 m_lTopIndex
+= m_VisibleItems
;
752 if(m_lTopIndex
>= m_lCount
-m_VisibleItems
)
753 m_lTopIndex
= m_lCount
-m_VisibleItems
;
764 if(m_lTopIndex
>= m_lCount
-m_VisibleItems
)
765 m_lTopIndex
= m_lCount
-m_VisibleItems
;
773 m_VertBar
.SetScrollPos(m_lTopIndex
,true);
775 if(m_oldlTopIndex
!= m_lTopIndex
)
779 /*********************************************************************/
781 CString
CACListWnd::GetNextString(int nChar
)
794 m_lSelItem
-= m_VisibleItems
;
800 m_lSelItem
+= m_VisibleItems
;
801 if(m_lSelItem
>= m_lCount
-1)
802 m_lSelItem
= m_lCount
-1;
810 m_lSelItem
= m_lCount
-1;
815 m_lSelItem
= m_lCount
-1;
817 if(m_lSelItem
>= m_lCount
)
820 if(EnsureVisible(m_lSelItem
,(m_lCount
> 50) ? false : true))
821 InvalidateAndScroll();
826 /*********************************************************************/
828 void CACListWnd::OnMouseMove(UINT nFlags
, CPoint point
)
830 CWnd::OnMouseMove(nFlags
, point
);
831 int sel
= HitTest(point
);
838 /*********************************************************************/
840 void CACListWnd::OnTimer(UINT_PTR nIDEvent
)
842 CWnd::OnTimer(nIDEvent
);
845 m_pEditParent
->GetParent()->GetWindowRect(m_ParentRect1
);
846 if(!m_ParentRect1
.EqualRect(m_ParentRect
))
850 /*********************************************************************/
852 void CACListWnd::OnGetMinMaxInfo(MINMAXINFO FAR
* lpMMI
)
857 long m_lMinY1
= GetSystemMetrics(SM_CYHSCROLL
)*2 + GetSystemMetrics(SM_CYSIZEFRAME
) + GetSystemMetrics(SM_CXHTHUMB
),
858 m_lMinY2
= m_lCount
* m_ItemHeight
+ (GetSystemMetrics(SM_CYBORDER
)*2);
860 if(m_VisibleItems
> m_lCount
-1 && m_lMinY2
< m_lMinY1
)
861 lpMMI
->ptMinTrackSize
.y
= m_lMinY2
;
863 lpMMI
->ptMinTrackSize
.y
= m_lMinY1
;
866 lpMMI
->ptMinTrackSize
.x
= GetSystemMetrics(SM_CXHSCROLL
)*4;
873 m_pEditParent
->GetWindowRect (&rc
);
874 lpMMI
->ptMinTrackSize
.x
= rc
.right
- rc
.left
;
878 CWnd::OnGetMinMaxInfo(lpMMI
);
881 /*********************************************************************/
883 int CACListWnd::CompareString(const void* p1
, const void* p2
)
885 return _stricmp( * ( char** ) p1
, * ( char** ) p2
);
888 /*********************************************************************/
890 void CACListWnd::SortList(CStringArray
& list
)
892 int m_Count
= static_cast<int>(list
.GetSize());
896 CStringArray m_Liste1
;
899 LPCWSTR
* ppSortArray
= new LPCWSTR
[m_Count
+1];
902 for (int i
= 0; i
< m_Count
; ++i
)
904 ppSortArray
[i
] = static_cast<LPCWSTR
>(m_Liste1
.GetAt(i
));
909 qsort(ppSortArray
, m_Count
, sizeof(LPCWSTR
), CompareString
);
911 for (int i
= 0; i
< m_Count
; ++i
)
913 list
.Add(static_cast<LPCWSTR
>(ppSortArray
[i
]));
915 m_Liste1
.RemoveAll();
916 delete [] ppSortArray
;
920 /*********************************************************************/
925 void CACListWnd::CopyList()
927 m_DisplayList
.Copy(m_SearchList
);
928 m_lCount
= static_cast<long>(m_DisplayList
.GetSize());
930 FindString(0, L
"", true);
933 /*********************************************************************/