// DDComboBox.cpp : Implementation of CDDComboBox
//
// Author : David Shepherd
// Copyright (c) 2003, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include ".\DDComboBox.h"
/////////////////////////////////////////////////////////////////////////////
// CDDComboBox
#pragma warning(push)
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
CDDComboBox::CDDComboBox() : m_ctlComboBox(_T("ComboBox"), this, 1)
#pragma warning(pop)
{
// initialise everything
m_bWindowOnly=TRUE;
m_Reserved0=0;
m_Reserved1=0;
m_Enabled=VARIANT_TRUE;
m_FillColor=MAKE_OLE_COLOR(COLOR_BTNFACE);
m_BackColor=MAKE_OLE_COLOR(COLOR_WINDOW);
m_ForeColor=MAKE_OLE_COLOR(COLOR_WINDOWTEXT);
m_Sorted=VARIANT_FALSE;
m_hAccel=NULL;
}
HRESULT CDDComboBox::FinalConstruct()
{
IMP_BEGIN
// create the background brush
CreateBackgroundBrush(m_BackColor);
// create the default font
m_spFontDisp=CreateDefaultFont();
IMP_END
return RetVal;
}
void CDDComboBox::FinalRelease()
{
}
const TCHAR* CDDComboBox::GetObjectFriendlyName()
{
// return the object friendly name
return _T("DDComboBox");
}
HRESULT CDDComboBox::OnBackColorChanging(OLE_COLOR newVal)
{
IMP_BEGIN
// create the background brush
// if this fails the background color will remain unchanged
CreateBackgroundBrush(newVal);
IMP_END
return RetVal;
}
void CDDComboBox::OnEnabledChanged()
{
TRY
// update the combo box
// this is done indirectly by enabling the parent window
if(IsWindow())
{
(void)EnableWindow(VB2B(m_Enabled));
}
CATCH_ALL
}
void CDDComboBox::OnBackColorChanged()
{
TRY
// update the combo box
if(m_ctlComboBox.IsWindow())
{
(void)m_ctlComboBox.Invalidate();
}
CATCH_ALL
}
void CDDComboBox::OnForeColorChanged()
{
TRY
// update the combo box
if(m_ctlComboBox.IsWindow())
{
(void)m_ctlComboBox.Invalidate();
}
CATCH_ALL
}
void CDDComboBox::CreateBackgroundBrush(OLE_COLOR BackColor)
{
// create the background brush
COLORREF RGBBackColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(BackColor,NULL,&RGBBackColor)))
{
throw std::exception();
}
CBrush Brush;
if(Brush.CreateSolidBrush(RGBBackColor)==NULL)
{
throw std::exception();
}
if(m_BackBrush!=NULL)
{
(void)m_BackBrush.DeleteObject();
}
m_BackBrush.Attach(Brush.Detach());
}
HRESULT CDDComboBox::OnDraw(ATL_DRAWINFO& di)
{
IMP_BEGIN
// device context wrapper
CDCHandle hDC(di.hdcDraw);
// bounding rectangle
CRect Rect=*(RECT*)di.prcBounds;
// fill in the background
COLORREF RGBFillColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_FillColor,NULL,&RGBFillColor)))
{
throw CHResult(E_FAIL);
}
hDC.FillSolidRect(Rect,RGBFillColor);
IMP_END
return RetVal;
}
BOOL CDDComboBox::PreTranslateAccelerator(LPMSG pMsg, HRESULT& hRet)
{
// this will be set TRUE if the key press was handled
BOOL Handled=FALSE;
TRY
// if the combo box has focus
if((GetFocus()==m_ctlComboBox or m_ctlComboBox.IsChild(GetFocus())) and
// and this is a key down message
pMsg->message==WM_KEYDOWN)
{
// get shift key modifiers
BOOL Ctrl = (GetKeyState(VK_CONTROL) & 0x80000000) ? TRUE : FALSE;
BOOL Shift = (GetKeyState(VK_SHIFT ) & 0x80000000) ? TRUE : FALSE;
BOOL Alt = (GetKeyState(VK_MENU ) & 0x80000000) ? TRUE : FALSE;
// the tab key is ignored since it is not valid for a combo box
// arrow key
if( pMsg->wParam==VK_LEFT or
pMsg->wParam==VK_RIGHT or
pMsg->wParam==VK_UP or
pMsg->wParam==VK_DOWN or
// other navigation
pMsg->wParam==VK_HOME or
pMsg->wParam==VK_END or
pMsg->wParam==VK_PRIOR or
pMsg->wParam==VK_NEXT or
// del
pMsg->wParam==VK_DELETE)
{
(void)SendMessage(pMsg->hwnd,
pMsg->message,pMsg->wParam,pMsg->lParam);
Handled=TRUE;
}
// cut / copy / paste / undo
if(Handled==FALSE)
{
if(::TranslateAccelerator(*this,m_hAccel,pMsg)!=0)
{
Handled=TRUE;
}
}
}
// if the key press was handled
if(Handled)
{
// signal success
hRet=S_OK;
}
CATCH_ALL
return Handled;
}
LRESULT CDDComboBox::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// create the combo box
if(m_ctlComboBox.Create(*this,CRect(0,0,0,0),_T(""),
// standard window styles
WS_CHILD|WS_VISIBLE|WS_VSCROLL|
// combo box styles
CBS_AUTOHSCROLL|CBS_DROPDOWN|CBS_HASSTRINGS|
// combo box styles that can only be applied at creation time
(m_Sorted ? CBS_SORT : 0),
// extended styles
0,
// id
IDC_COMBOBOX)==NULL)
{
throw std::exception();
}
// update the combo box
if(!SUCCEEDED(put_Enabled(m_Enabled)))
{
throw std::exception();
}
if(!SUCCEEDED(put_Font(m_spFontDisp)))
{
throw std::exception();
}
// load the combo box accelerators
m_hAccel=LoadAccelerators(_pModule->GetResourceInstance(),
MAKEINTRESOURCE(IDA_DDCOMBOBOX));
if(m_hAccel==NULL)
{
throw std::exception();
}
CATCH_ALL
return Caught ? -1 : 0;
}
LRESULT CDDComboBox::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// generated by the ATL control wizard
LRESULT lRes = CComControl<CDDComboBox>::OnSetFocus(uMsg, wParam, lParam, bHandled);
if (m_bInPlaceActive)
{
if(!IsChild(::GetFocus()))
m_ctlComboBox.SetFocus();
}
return lRes;
}
LRESULT CDDComboBox::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// to prevent flicker do not erase the background
return TRUE;
}
LRESULT CDDComboBox::OnCtlColorComboBox(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// wrap the device context
CDCHandle hDC=(HDC)wParam;
// set the background color
COLORREF RGBBackColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_BackColor,NULL,&RGBBackColor)))
{
throw std::exception();
}
if(hDC.SetBkColor(RGBBackColor)==CLR_INVALID)
{
throw std::exception();
}
// set the foreground color
COLORREF RGBForeColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_ForeColor,NULL,&RGBForeColor)))
{
throw std::exception();
}
if(hDC.SetTextColor(RGBForeColor)==CLR_INVALID)
{
throw std::exception();
}
CATCH_ALL
// return the background brush
// todo : recover correctly if an exception is thrown
return (LRESULT)(HBRUSH)m_BackBrush;
}
LRESULT CDDComboBox::OnFireSelectionChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
PROPERTY_CHANGED(DISPID_TEXT)
// fire the selection change event
__raise SelectionChange();
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnComboBoxEditChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
PROPERTY_CHANGED(DISPID_TEXT)
// fire the edit change event
__raise EditChange();
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnComboBoxSelChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
// ensure the edit portion of the combo box is updated before firing the
// selection change event
(void)PostMessage(WM_FIRE_SELECTION_CHANGE);
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnCut(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
// cut
if(m_ctlComboBox.IsWindow())
{
(void)m_ctlComboBox.SendMessage(WM_CUT);
}
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnCopy(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
// copy
if(m_ctlComboBox.IsWindow())
{
(void)m_ctlComboBox.SendMessage(WM_COPY);
}
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnPaste(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
// paste
if(m_ctlComboBox.IsWindow())
{
(void)m_ctlComboBox.SendMessage(WM_PASTE);
}
CATCH_ALL
return 0;
}
LRESULT CDDComboBox::OnUndo(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
// todo : implement this by sending WM_UNDO to the edit box
ATLASSERT(FALSE);
CATCH_ALL
return 0;
}
STDMETHODIMP CDDComboBox::SetClientSite(IOleClientSite *pClientSite)
{
IMP_BEGIN
// call the base class
HRESULT hr=IOleObjectImpl<CDDComboBox>::SetClientSite(pClientSite);
if(!SUCCEEDED(hr))
{
throw CHResult(hr);
}
// set the fill color based on the container ambient properties
OLE_COLOR FillColor=RGB(0,0,0);
if(SUCCEEDED(GetAmbientBackColor(FillColor)))
{
(void)put_FillColor(FillColor);
}
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::SetObjectRects(LPCRECT prcPos,LPCRECT prcClip)
{
// generated by the ATL control wizard
IOleInPlaceObjectWindowlessImpl<CDDComboBox>::SetObjectRects(prcPos, prcClip);
int cx, cy;
cx = prcPos->right - prcPos->left;
cy = prcPos->bottom - prcPos->top;
::SetWindowPos(m_ctlComboBox.m_hWnd, NULL, 0,
0, cx, 200/*changed from cy*/, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}
STDMETHODIMP CDDComboBox::get_Font(IFontDisp** pVal)
{
IMP_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// get the font
*pVal=m_spFontDisp;
if(*pVal!=NULL) // add ref it
{
(void)(*pVal)->AddRef();
}
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::put_Font(IFontDisp* newVal)
{
IMP_BEGIN
REQUEST_EDIT(DISPID_FONT)
// check parameters
if(newVal==NULL)
{
throw CHResult(E_POINTER);
}
// clone the passed font and delegate to putref_Font
if(!SUCCEEDED(putref_Font(CloneFont(newVal))))
{
throw CHResult(E_FAIL);
}
PROPERTY_CHANGED(DISPID_FONT)
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::putref_Font(IFontDisp* newVal)
{
IMP_BEGIN
REQUEST_EDIT(DISPID_FONT)
// check parameters
if(newVal==NULL)
{
throw CHResult(E_POINTER);
}
// set the font
CComQIPtr<IFont> spFont(newVal);
if(spFont==NULL)
{
throw CHResult(E_FAIL);
}
CFontHandle hFont=NULL;
if(!SUCCEEDED(spFont->get_hFont(&hFont.m_hFont)))
{
throw CHResult(E_FAIL);
}
if(m_ctlComboBox.IsWindow())
{
m_ctlComboBox.SetFont(hFont);
}
m_spFontDisp=newVal;
PROPERTY_CHANGED(DISPID_FONT)
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::get_Text(BSTR* pVal)
{
IMP_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// check the combo box is created
if(m_ctlComboBox.IsWindow()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the text
if(m_ctlComboBox.GetWindowText(pVal)==FALSE)
{
throw CHResult(E_FAIL);
}
// todo : update m_Text
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::put_Text(BSTR newVal)
{
IMP_BEGIN
// request edit can not be applied to this property since the
// combo box text can be changed by the user at any time
USES_CONVERSION;
// check the combo box is created
if(m_ctlComboBox.IsWindow()==FALSE)
{
throw CHResult(E_FAIL);
}
// set the text
if(m_ctlComboBox.SetWindowText(W2CT(BSTR2W(newVal)))==FALSE)
{
throw CHResult(E_FAIL);
}
// todo : update m_Text
PROPERTY_CHANGED(DISPID_TEXT)
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::AddItem(BSTR Text)
{
IMP_BEGIN
USES_CONVERSION;
// check parameters
if(wcslen(BSTR2W(Text))==0)
{
throw CHResult(E_INVALIDARG);
}
// check the combo box is created
if(m_ctlComboBox.IsWindow()==FALSE)
{
throw CHResult(E_FAIL);
}
// wrap the combo box
CComboBox ComboBox(m_ctlComboBox);
// ensure the item does not already exist
LONG Ix=ComboBox.FindStringExact(-1,W2CT(BSTR2W(Text)));
if(Ix!=CB_ERR)
{
throw CHResult(E_FAIL);
}
// add the item
Ix=ComboBox.AddString(W2CT(BSTR2W(Text)));
if(Ix==CB_ERR or Ix==CB_ERRSPACE)
{
throw CHResult(E_FAIL);
}
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::RemoveItem(BSTR Text)
{
IMP_BEGIN
USES_CONVERSION;
// check parameters
if(wcslen(BSTR2W(Text))==0)
{
throw CHResult(E_INVALIDARG);
}
// check the combo box is created
if(m_ctlComboBox.IsWindow()==FALSE)
{
throw CHResult(E_FAIL);
}
// wrap the combo box
CComboBox ComboBox(m_ctlComboBox);
// ensure the item exists
LONG Ix=ComboBox.FindStringExact(-1,W2CT(BSTR2W(Text)));
if(Ix==CB_ERR)
{
throw CHResult(E_FAIL);
}
// remove the item
if(ComboBox.DeleteString(Ix)==CB_ERR)
{
throw CHResult(E_FAIL);
}
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::Clear()
{
IMP_BEGIN
// check the combo box is created
if(m_ctlComboBox.IsWindow()==FALSE)
{
throw CHResult(E_FAIL);
}
// wrap the combo box
CComboBox ComboBox(m_ctlComboBox);
// clear the combo box
ComboBox.ResetContent();
IMP_END
return RetVal;
}
STDMETHODIMP CDDComboBox::get__Default(BSTR* pVal)
{
// delegate to the required default property
return get_Text(pVal);
}
STDMETHODIMP CDDComboBox::put__Default(BSTR newVal)
{
// delegate to the required default property
return put_Text(newVal);
}