// XComboList.cpp // // Author: Hans Dietrich // hdietrich2@hotmail.com // // This software is released into the public domain. // You are free to use it in any way you like. // // This software is provided "as is" with no expressed // or implied warranty. I accept no liability for any // damage or loss of business that this software may cause. // /////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "XComboList.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif UINT NEAR WM_XCOMBOLIST_VK_RETURN = ::RegisterWindowMessage(_T("WM_XCOMBOLIST_VK_RETURN")); UINT NEAR WM_XCOMBOLIST_VK_ESCAPE = ::RegisterWindowMessage(_T("WM_XCOMBOLIST_VK_ESCAPE")); UINT NEAR WM_XCOMBOLIST_KEYDOWN = ::RegisterWindowMessage(_T("WM_XCOMBOLIST_KEYDOWN")); UINT NEAR WM_XCOMBOLIST_LBUTTONUP = ::RegisterWindowMessage(_T("WM_XCOMBOLIST_LBUTTONUP")); BEGIN_MESSAGE_MAP(CXComboList, CWnd) //{{AFX_MSG_MAP(CXComboList) ON_WM_LBUTTONDOWN() ON_WM_KILLFOCUS() ON_WM_CREATE() ON_WM_VSCROLL() ON_WM_DESTROY() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////////// // ctor CXComboList::CXComboList(CWnd *pParent) : m_pParent(pParent), m_nCount(0), m_bFirstTime(TRUE) { ASSERT(m_pParent); } CXComboList::~CXComboList() { } /////////////////////////////////////////////////////////////////////////////// // SetActive void CXComboList::SetActive(int nScrollBarWidth) { XLISTCTRL_TRACE(_T("in CXComboList::SetActive\n")); if (!::IsWindow(m_ListBox.m_hWnd)) return; m_ListBox.SetFocus(); if (m_bFirstTime) { m_bFirstTime = FALSE; CRect rect; GetWindowRect(&rect); // set listbox size according to item height int nItemHeight = m_ListBox.GetItemHeight(0); CRect lbrect; GetClientRect(&lbrect); lbrect.top += 1; lbrect.bottom = lbrect.top + (rect.Height() / nItemHeight) * nItemHeight; lbrect.left += 1; lbrect.right -= nScrollBarWidth; int nItemsInView = (lbrect.Height()) / nItemHeight; // set size of listbox wrapper (from size of listbox) rect.bottom = rect.top + lbrect.Height() + 4; MoveWindow(&rect); m_ListBox.MoveWindow(&lbrect); m_ListBox.BringWindowToTop(); // set size and position for vertical scroll bar CRect sbrect; sbrect = lbrect; sbrect.left = lbrect.right; sbrect.right += nScrollBarWidth; m_wndSBVert.MoveWindow(&sbrect); SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_ALL; m_wndSBVert.GetScrollInfo(&si); // set info for scrollbar si.nMin = 0; si.nMax = m_ListBox.GetCount(); if (si.nMax < 0) si.nMax = 1; si.nPage = nItemsInView; int nCurSel = m_ListBox.GetCurSel(); if (nCurSel == LB_ERR || nCurSel < 0) nCurSel = 0; si.nPos = nCurSel; // set top index, to force selected item to be in view m_ListBox.SetTopIndex(nCurSel > 0 ? nCurSel - 1 : 0); if (si.nPos < 0) si.nPos = 0; m_wndSBVert.SetScrollInfo(&si); m_wndSBVert.SetScrollPos(si.nPos, TRUE); //RedrawWindow(); SetTimer(1, 80, NULL); } } /////////////////////////////////////////////////////////////////////////////// // GetScrollBarCtrl CScrollBar* CXComboList::GetScrollBarCtrl(int nBar) { UNUSED_ALWAYS(nBar); return &m_wndSBVert; } /////////////////////////////////////////////////////////////////////////////// // SendRegisteredMessage void CXComboList::SendRegisteredMessage(UINT nMsg, WPARAM wParam, LPARAM lParam) { CWnd *pWnd = m_pParent; if (pWnd) pWnd->SendMessage(nMsg, wParam, lParam); } /////////////////////////////////////////////////////////////////////////////// // OnLButtonDown void CXComboList::OnLButtonDown(UINT nFlags, CPoint point) { SendRegisteredMessage(WM_XCOMBOLIST_LBUTTONUP, 0, 0); CWnd::OnLButtonUp(nFlags, point); //????? why up } /////////////////////////////////////////////////////////////////////////////// // PreTranslateMessage BOOL CXComboList::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYDOWN: { /////////////////////////////////////////////////////////////////// // we need to trap all cursor keys & alpha keys to reposition the // scroll bar /////////////////////////////////////////////////////////////////// //XLISTCTRL_TRACE(" WM_KEYDOWN\n"); SCROLLINFO si = { sizeof(SCROLLINFO), SIF_ALL | SIF_DISABLENOSCROLL, }; m_wndSBVert.GetScrollInfo(&si); BOOL bSetScrollInfo = FALSE; int nIndex = 0; if (::IsWindow(m_ListBox.m_hWnd)) nIndex = m_ListBox.GetCurSel(); if (nIndex == LB_ERR || nIndex < 0) nIndex = 0; // use index from listbox, because scroll position cannot be relied // upon here switch (pMsg->wParam) { case VK_RETURN: SendRegisteredMessage(WM_XCOMBOLIST_VK_RETURN, 0, 0); break; case VK_ESCAPE: SendRegisteredMessage(WM_XCOMBOLIST_VK_ESCAPE, 0, 0); break; // handle scrolling messages case VK_DOWN: si.nPos = nIndex + 1; bSetScrollInfo = TRUE; break; case VK_END: si.nPos = si.nMax; bSetScrollInfo = TRUE; break; case VK_HOME: si.nPos = 0; bSetScrollInfo = TRUE; break; case VK_NEXT: // PAGE DOWN si.nPos = nIndex + (si.nPage-1); bSetScrollInfo = TRUE; break; case VK_PRIOR: // PAGE UP si.nPos = nIndex - (si.nPage - 1); bSetScrollInfo = TRUE; break; case VK_UP: si.nPos = nIndex - 1; bSetScrollInfo = TRUE; break; default: if (pMsg->wParam >= 0x41/*VK_A*/ && pMsg->wParam <= 0x5A/*VK_Z*/) { // this was an alpha key - try to find listbox index CString strAlpha; strAlpha = (_TCHAR) pMsg->wParam; int nIndex2 = 0; if (::IsWindow(m_ListBox.m_hWnd)) nIndex2 = m_ListBox.FindString(nIndex, strAlpha); if (nIndex2 != LB_ERR) { si.nPos = nIndex2; bSetScrollInfo = TRUE; } } break; } if (bSetScrollInfo) { // let parent know the selection has changed SendRegisteredMessage(WM_XCOMBOLIST_KEYDOWN, 0, 0); // update scrollbar if (si.nPos < 0) si.nPos = 0; if (si.nPos > si.nMax) si.nPos = si.nMax; m_wndSBVert.SetScrollInfo(&si); } break; } case WM_LBUTTONUP: SendRegisteredMessage(WM_XCOMBOLIST_LBUTTONUP, 0, 0); break; } return CWnd::PreTranslateMessage(pMsg); } /////////////////////////////////////////////////////////////////////////////// // OnKillFocus void CXComboList::OnKillFocus(CWnd* pNewWnd) { XLISTCTRL_TRACE(_T("in CXComboList::OnKillFocus\n")); CWnd::OnKillFocus(pNewWnd); m_nCount++; if (m_nCount > 2) { SendRegisteredMessage(WM_XCOMBOLIST_VK_ESCAPE, 0, 0); } } /////////////////////////////////////////////////////////////////////////////// // OnCreate int CXComboList::OnCreate(LPCREATESTRUCT lpCreateStruct) { XLISTCTRL_TRACE(_T("in CXComboList::OnCreate\n")); if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; CRect rect2(0,0,0,0); // create the listbox that we're wrapping VERIFY(m_ListBox.Create(WS_VISIBLE|WS_CHILD|LBS_NOINTEGRALHEIGHT/*|WS_BORDER*/, rect2, this, 0)); // create the vertical scrollbar VERIFY(m_wndSBVert.Create(WS_VISIBLE|WS_CHILD|SBS_VERT, rect2, this, 0)); // set font from parent CFont *font = GetParent()->GetFont(); if (font) { SetFont(font, FALSE); m_wndSBVert.SetFont(font, FALSE); } return 0; } /////////////////////////////////////////////////////////////////////////////// // OnVScroll void CXComboList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*) { if (!::IsWindow(m_ListBox.m_hWnd)) return; // forward scroll message to listbox const MSG* pMsg = GetCurrentMessage(); m_ListBox.SendMessage(WM_VSCROLL, pMsg->wParam, pMsg->lParam); SCROLLINFO si = { sizeof(SCROLLINFO), SIF_ALL | SIF_DISABLENOSCROLL, }; m_wndSBVert.GetScrollInfo(&si); switch (nSBCode) { case SB_BOTTOM: // scroll to bottom si.nPos = si.nMax; break; case SB_TOP: // scroll to top si.nPos = 0; break; case SB_PAGEDOWN: // scroll one page down si.nPos += si.nPage; break; case SB_PAGEUP: // scroll one page up si.nPos -= si.nPage; break; case SB_LINEDOWN: // scroll one line up si.nPos += 1; break; case SB_LINEUP: // scroll one line up si.nPos -= 1; break; case SB_THUMBTRACK: // drag scroll box to specified position. The // current position is provided in nPos case SB_THUMBPOSITION: // scroll to the absolute position. The current // position is provided in nPos si.nPos = nPos; break; case SB_ENDSCROLL: // end scroll return; default: break; } if (si.nPos < 0) si.nPos = 0; if (si.nPos > si.nMax) si.nPos = si.nMax; m_wndSBVert.SetScrollInfo(&si); } /////////////////////////////////////////////////////////////////////////////// // OnDestroy void CXComboList::OnDestroy() { XLISTCTRL_TRACE(_T("in CXComboList::OnDestroy\n")); KillTimer(1); if (::IsWindow(m_ListBox.m_hWnd)) m_ListBox.DestroyWindow(); CWnd::OnDestroy(); } /////////////////////////////////////////////////////////////////////////////// // OnTimer void CXComboList::OnTimer(UINT nIDEvent) { UNUSED_ALWAYS(nIDEvent); if (!::IsWindow(m_ListBox.m_hWnd)) return; // get current mouse position POINT point; ::GetCursorPos(&point); ScreenToClient(&point); BOOL bOutside; int nIndex = m_ListBox.ItemFromPoint(point, bOutside); //XLISTCTRL_TRACE(" nIndex=%d bOutside=%d\n", nIndex, bOutside); if (!bOutside) { int nCurSel = m_ListBox.GetCurSel(); if (nIndex != nCurSel) if (nIndex >= 0 && nIndex < m_ListBox.GetCount()) m_ListBox.SetCurSel(nIndex); } }