// ========================================================================== // Class Implementation : // COXCoolComboEdit & COXCoolComboBox // ========================================================================== // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXCoolComboBox.h" #include "UTB64Bit.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // COXCoolComboEdit void COXCoolComboEdit::SetCombo(COXCoolComboBox* pCombo) { ASSERT(pCombo!=NULL); m_pCombo=pCombo; } COXCoolComboBox* COXCoolComboEdit::GetCombo() { return m_pCombo; } void COXCoolComboEdit::OnFocusChange(CWnd* pWndGainFocus, CWnd* pWndLooseFocus) { ASSERT(m_pCombo!=NULL); if((pWndGainFocus==this && !m_bInHotState) || (pWndLooseFocus==this && m_bInHotState)) { m_pCombo->OnChangeHotState(!m_bInHotState); } COXCoolCtrl::OnFocusChange(pWndGainFocus, pWndLooseFocus); } BEGIN_MESSAGE_MAP(COXCoolComboEdit, CEdit) //{{AFX_MSG_MAP(COXCoolComboEdit) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXCoolComboBox COXCoolComboBox::COXCoolComboBox() { m_nDefaultFontHeight=0; m_nDefaultFontHeightSansLeading=0; } COXCoolComboBox::~COXCoolComboBox() { } BEGIN_MESSAGE_MAP(COXCoolComboBox, CComboBox) //{{AFX_MSG_MAP(COXCoolComboBox) ON_WM_PAINT() ON_WM_PARENTNOTIFY() ON_WM_CTLCOLOR() //}}AFX_MSG_MAP ON_CONTROL_REFLECT_EX(CBN_CLOSEUP,OnUpdateCombo) ON_MESSAGE(WM_SETFONT,OnSetFont) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXCoolComboBox message handlers BOOL COXCoolComboBox::OnUpdateCombo() { CheckMousePos(); RedrawWindow(); return TRUE; } // This message is handle to get the HWND of the listbox of the combobox // as there is no otherway to get it(refer to Q65881, Q131845 in Knowledge Base) HBRUSH COXCoolComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hBrush = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor); // Handle only if the type of control is Listbox only if((nCtlColor == CTLCOLOR_LISTBOX) && !(GetStyle()&CBS_OWNERDRAWFIXED || GetStyle()&CBS_OWNERDRAWVARIABLE)) { if(!::IsWindow(m_listBox.GetSafeHwnd())) { if(m_listBox.SubclassWindow(pWnd->GetSafeHwnd())) { CString sTooltipText; if(GetTooltipText(sTooltipText)) VERIFY(m_listBox.SetTooltipText(sTooltipText)); m_listBox.RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_FRAME); } } } return hBrush; } void COXCoolComboBox::OnParentNotify(UINT message, LPARAM lParam) { COXCoolCtrl::OnParentNotify(message, lParam); // TODO: Add your message handler code here if(LOWORD(message)==WM_CREATE) { if(HIWORD(message)==IDW_OXCOMBOLISTBOX) SubclassListBoxCtrl(); if(HIWORD(message)==IDW_OXCOMBOEDIT) SubclassEditCtrl(); } } LRESULT COXCoolComboBox::OnSetFont(WPARAM wParam,LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); Default(); CDC* pDC=GetDC(); pDC->SelectObject(GetFont()); TEXTMETRIC tm; pDC->GetTextMetrics(&tm); m_nDefaultFontHeight=tm.tmHeight; m_nDefaultFontHeightSansLeading=pDC->GetTextExtent(_T("ygaQ!|")).cy; ReleaseDC(pDC); int nCurrentHeight=PtrToInt(SendMessage(CB_GETITEMHEIGHT,(WPARAM)-1,0)); if (nCurrentHeight!=m_nDefaultFontHeight+1) SendMessage(CB_SETITEMHEIGHT,(WPARAM)-1,m_nDefaultFontHeight+1); return 0; } void COXCoolComboBox::OnPaint() { if(!IsInCoolState()) { Default(); return; } // set up all the pens we are likely to need HPEN penDefault=(HPEN)GetStockObject(BLACK_PEN); static CPen penHilite(PS_SOLID,1,GetSysColor(COLOR_3DHIGHLIGHT)); static CPen penShadow(PS_SOLID,1,GetSysColor(COLOR_3DSHADOW)); static CPen penFace3D(PS_SOLID,1,GetSysColor(COLOR_3DFACE)); static CPen penDkShad3D(PS_SOLID,1,GetSysColor(COLOR_3DDKSHADOW)); // store margins for future use int nLeftMargin=GetLeftMargin(); int nRightMargin=GetRightMargin(); int nTopMargin=GetTopMargin(); int nBottomMargin=GetBtmMargin(); CRect button_rect=GetButtonRect(); const BOOL bSimpleCombo=((GetStyle()&0x000f)==CBS_SIMPLE); const BOOL bIsEnabled=IsWindowEnabled(); const BOOL bIsHot=IsInHotState() & bIsEnabled; // paint background CPaintDC paintdc(this); CDC* dc=&paintdc; CRect rect, rectClient, rectInternal; // temp rect, client rect, internal rect respectively GetClientRect(rectClient); rectClient.right-=::GetSystemMetrics(SM_CXVSCROLL)-GetButtonWidth(); if(bSimpleCombo) { ASSERT(::IsWindow(m_edit.GetSafeHwnd())); CRect rectEdit; m_edit.GetWindowRect(rectEdit); rectClient.bottom=rectClient.top+rectEdit.Height()+5; } // set up internal rectangle rectInternal=rectClient; rectInternal.left+=nLeftMargin; rectInternal.top+=nTopMargin; rectInternal.right-=nRightMargin; rectInternal.bottom-=nBottomMargin; rect=rectInternal; if(!button_rect.IsRectEmpty()) rect.right=button_rect.left; if(!bIsEnabled) ::FillRect(dc->m_hDC,&rect,GetSysColorBrush(COLOR_3DFACE)); else ::FillRect(dc->m_hDC,&rect,GetSysColorBrush(COLOR_WINDOW)); // do default painting if(!::IsWindow(m_edit.GetSafeHwnd())) { DRAWITEMSTRUCT dis={ sizeof(&dis) }; dis.CtlType=ODT_COMBOBOX; dis.CtlID=GetWindowLongPtr(m_hWnd, GWL_ID); dis.itemID=GetCurSel(); dis.itemAction=ODA_DRAWENTIRE; dis.hwndItem=m_hWnd; dis.hDC=dc->m_hDC; dis.rcItem=CRect(nLeftMargin+1,nTopMargin+1, button_rect.left-1,rectClient.bottom-nBottomMargin-1); dis.itemData=GetItemData(GetCurSel()); dis.itemState=((::GetFocus()==GetSafeHwnd() && !IsDroppedDown()) ? ODS_FOCUS : 0); DrawItem(&dis); } // now do our painting - first borderect... // but first exclude the 'real' client area from the DC dc->SelectClipRgn(NULL); // from drawing background dc->ExcludeClipRect(&rectInternal); // draw border if(!bIsHot) { // now paint background if(bSimpleCombo) FillRect(dc->m_hDC, &rectClient, ::GetSysColorBrush(COLOR_WINDOW)); else FillRect(dc->m_hDC, &rectClient, ::GetSysColorBrush(COLOR_3DFACE)); // fill in small area to the left of edit window in appropriate colour, // to make window look bigger rect=rectClient; rect.top+=nTopMargin; rect.left+=nLeftMargin-1; rect.right=rect.left+1; rect.bottom-=nBottomMargin; if(bIsEnabled) FillRect(dc->m_hDC, &rect, GetSysColorBrush(COLOR_WINDOW)); else FillRect(dc->m_hDC, &rect, GetSysColorBrush(COLOR_3DFACE)); } else { // HOT case - difficult to describe, but reasonably obvious rect=rectClient; rect.left++; rect.right--; rect.top++; rect.bottom-=1+EXTRA_BOTTOM_MARGIN; // paint background FillRect(dc->m_hDC, &rect, GetSysColorBrush(COLOR_3DFACE)); // and border rect=rectClient; SelectObject(dc->m_hDC,penShadow); dc->MoveTo(0,0); dc->LineTo(rect.right,0); dc->MoveTo(0,0); dc->LineTo(0,rect.bottom-1-EXTRA_BOTTOM_MARGIN); SelectObject(dc->m_hDC,penHilite); dc->LineTo(rect.right-1,rect.bottom-1-EXTRA_BOTTOM_MARGIN); dc->LineTo(rect.right-1,rect.top); // and paint extra margin if(EXTRA_BOTTOM_MARGIN) { rect=rectClient; rect.top=rect.bottom-EXTRA_BOTTOM_MARGIN; FillRect(dc->m_hDC, &rect, GetSysColorBrush(COLOR_3DFACE)); } } // unselect the clip region SelectClipRgn(dc->m_hDC, NULL); // now draw highlight border, if disabled if(!bIsEnabled) { SelectObject(dc->m_hDC, GetStockObject(NULL_BRUSH)); SelectObject(dc->m_hDC, penHilite); dc->Rectangle(rectInternal.left-1, rectInternal.top-1, rectInternal.right+1, rectInternal.bottom+1); } // and now for the button BOOL pressed=IsDroppedDown(); rect=button_rect; // do the background if(bIsHot) { rect.bottom--; rect.right++; } if(bIsHot || pressed || !bIsEnabled) { ::FillRect(dc->m_hDC, &rect, GetSysColorBrush(COLOR_3DFACE)); } else { if(bIsEnabled) { // cold and unpressed SelectObject(dc->m_hDC, penHilite); SelectObject(dc->m_hDC, GetSysColorBrush(COLOR_3DFACE)); ::Rectangle(dc->m_hDC, rect.left, rect.top, rect.right, rect.bottom); } } rect=button_rect; // set again if(bIsHot || pressed) { if(pressed) { SelectObject(dc->m_hDC, penShadow); dc->MoveTo(rect.left , rect.top); dc->LineTo(rect.right, rect.top); dc->MoveTo(rect.left , rect.top); dc->LineTo(rect.left , rect.bottom); } else { SelectObject(dc->m_hDC, penHilite); dc->MoveTo(rect.right, rect.top); dc->LineTo(rect.left+1, rect.top); dc->LineTo(rect.left+1, rect.bottom); SelectObject(dc->m_hDC, penFace3D); dc->MoveTo(rect.left, rect.top); dc->LineTo(rect.left, rect.bottom); SelectObject(dc->m_hDC, penShadow); dc->MoveTo(rect.left , rect.bottom - 1); dc->LineTo(rect.right-1, rect.bottom - 1); dc->LineTo(rect.right-1, rect.top); } SelectObject(dc->m_hDC, penDefault); } // now draw shadowy arrow SelectObject(dc->m_hDC, penDkShad3D); SelectObject(dc->m_hDC, ::GetSysColorBrush(COLOR_3DDKSHADOW)); POINT p[3]; p[0].x=rect.left +((rect.right - rect.left) / 3); p[1].x=rect.left +(2 *(rect.right - rect.left) / 3) + 1; p[2].x=(p[0].x + p[1].x) / 2; if(((p[1].x - p[0].x) % 2)) p[1].x--; p[0].y=(rect.top + rect.bottom / 2) -(p[1].x - p[0].x) / 4 - 1; p[1].y=p[0].y; p[2].y=p[0].y +(p[1].x - p[0].x) / 2; if(pressed) // offset by a pixel { p[0].x++; p[0].y++; p[1].x++; p[1].y++; p[2].x++; p[2].y++; } Polygon(dc->m_hDC, p, 3); } LRESULT COXCoolComboBox::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif if(message==WM_NCPAINT) { return CComboBox::WindowProc(message,wParam,lParam); } else { return COXCoolCtrl::WindowProc(message,wParam,lParam); } } void COXCoolComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: Add your code to draw the specified item int nItem=GetCurSel(); CString sText(_T("")); if(lpDrawItemStruct->itemID!=-1) nItem=lpDrawItemStruct->itemID; if(nItem!=-1) GetLBText(nItem,sText); BOOL bHighlight=(lpDrawItemStruct->itemState & (ODS_FOCUS | ODS_SELECTED)); BOOL bFocus=(lpDrawItemStruct->itemState & ODS_FOCUS); CRect rect=lpDrawItemStruct->rcItem; // fill background ::FillRect(lpDrawItemStruct->hDC,rect, ::GetSysColorBrush(!IsWindowEnabled() ? COLOR_3DFACE : (bHighlight ? COLOR_HIGHLIGHT : COLOR_WINDOW))); if(!sText.IsEmpty()) { if(bFocus) ::DrawFocusRect(lpDrawItemStruct->hDC,rect); // select font ::SelectObject(lpDrawItemStruct->hDC,(HFONT)SendMessage(WM_GETFONT,0,0)); // set text attributes ::SetBkMode(lpDrawItemStruct->hDC,TRANSPARENT); if(bHighlight) ::SetTextColor(lpDrawItemStruct->hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT)); else if(IsWindowEnabled()) ::SetTextColor(lpDrawItemStruct->hDC, ::GetSysColor(COLOR_WINDOWTEXT)); else ::SetTextColor(lpDrawItemStruct->hDC, ::GetSysColor(COLOR_3DSHADOW)); rect.DeflateRect(1,1); // draw the text ::DrawText(lpDrawItemStruct->hDC,sText,sText.GetLength(),rect, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX); } } void COXCoolComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpms) { if(lpms->itemID<0) lpms->itemHeight=m_nDefaultFontHeight+1; else lpms->itemHeight=m_nDefaultFontHeightSansLeading+1; } int COXCoolComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpcs) { // default behaviour CString sText1(_T("")); CString sText2(_T("")); if(lpcs->itemID1!=-1) GetLBText(lpcs->itemID1,sText1); if(lpcs->itemID2!=-1) GetLBText(lpcs->itemID2,sText2); return sText1.CompareNoCase(sText2); } void COXCoolComboBox::OnChangeHotState(BOOL bInHotState) { if(!bInHotState && ::GetFocus()==m_edit.GetSafeHwnd()) return; COXCoolCtrl::OnChangeHotState(bInHotState); if(!IsDroppedDown()) { RedrawWindow(); } } BOOL COXCoolComboBox::OnChangeTooltipText(const CString& sTooltipText) { BOOL bResult=TRUE; if(::IsWindow(m_edit.GetSafeHwnd())) bResult&=m_edit.SetTooltipText(sTooltipText); if(::IsWindow(m_listBox.GetSafeHwnd())) bResult&=m_listBox.SetTooltipText(sTooltipText); return bResult; } BOOL COXCoolComboBox::InitializeCoolCtrl() { if(!COXCoolCtrl::InitializeCoolCtrl()) return FALSE; CDC* pDC=GetDC(); pDC->SelectObject(GetFont()); TEXTMETRIC tm; pDC->GetTextMetrics(&tm); m_nDefaultFontHeight=tm.tmHeight; m_nDefaultFontHeightSansLeading=pDC->GetTextExtent(_T("ygaQ!|")).cy; ReleaseDC(pDC); if(!SubclassEditCtrl()) return FALSE; if(!SubclassListBoxCtrl()) return FALSE; return TRUE; } void COXCoolComboBox::PreSubclassWindow() { COXCoolCtrl::PreSubclassWindow(); if(m_bInitializedCoolCtrl) { if(GetStyle()&(CBS_OWNERDRAWFIXED|CBS_OWNERDRAWVARIABLE)) { _AFX_THREAD_STATE* pThreadState=AfxGetThreadState(); // hook not already in progress if(pThreadState->m_pWndInit==NULL) { SendMessage(CB_SETITEMHEIGHT,(WPARAM)-1,m_nDefaultFontHeight+1); } } } } int COXCoolComboBox::GetButtonWidth() const { return ::GetSystemMetrics(SM_CXVSCROLL); } CRect COXCoolComboBox::GetButtonRect() const { CRect rect(0,0,0,0); BOOL bSimpleCombo=((GetStyle()&0x000f)==CBS_SIMPLE); if(bSimpleCombo) return rect; GetClientRect(&rect); int nRightMargin=GetRightMargin(); int nTopMargin=GetTopMargin(); int nBottomMargin=GetBtmMargin(); int nButtonWidth=GetButtonWidth(); rect.left=rect.right-nRightMargin-::GetSystemMetrics(SM_CXVSCROLL); rect.top+=nTopMargin; rect.right=rect.left+nButtonWidth; rect.bottom-=nBottomMargin; if(rect.left<0) rect.left=0; return rect; } BOOL COXCoolComboBox::SubclassEditCtrl() { ASSERT(::IsWindow(GetSafeHwnd())); HWND hWnd; GetDlgItem(IDW_OXCOMBOEDIT,&hWnd); if(hWnd!=NULL) { m_edit.SetCombo(this); if(!m_edit.SubclassWindow(hWnd)) return FALSE; CString sTooltipText; if(GetTooltipText(sTooltipText)) VERIFY(m_edit.SetTooltipText(sTooltipText)); } return TRUE; } BOOL COXCoolComboBox::SubclassListBoxCtrl() { ASSERT(::IsWindow(GetSafeHwnd())); if(GetStyle()&CBS_OWNERDRAWFIXED || GetStyle()&CBS_OWNERDRAWVARIABLE) return TRUE; HWND hWnd; GetDlgItem(IDW_OXCOMBOLISTBOX,&hWnd); if(hWnd!=NULL) { if(!m_listBox.SubclassWindow(hWnd)) return FALSE; CString sTooltipText; if(GetTooltipText(sTooltipText)) VERIFY(m_listBox.SetTooltipText(sTooltipText)); } return TRUE; }