home *** CD-ROM | disk | FTP | other *** search
- /*
- * EVENTS.CPP
- * Patron Chapter 24
- *
- * Implementation of the Events dialog box, the CEventMap class,
- * and the events IDispatch on a tenant control site.
- *
- * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
- *
- * Kraig Brockschmidt, Microsoft
- * Internet : kraigb@microsoft.com
- * Compuserve: >INTERNET:kraigb@microsoft.com
- */
-
-
- #include "patron.h"
-
-
- /*
- * EventsDlgProc
- *
- * Purpose:
- * Dialog procedure for the dialog in which the user can assign
- * actions to events.
- */
-
- BOOL APIENTRY EventsDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam
- , LPARAM lParam)
- {
- PCEventMap pEM=NULL;
- HWND hList;
- UINT i, iEvent;
-
- COMMANDPARAMS(wID, wCode, hWndMsg);
-
- #ifdef WIN32
- pEM=(PCEventMap)GetProp(hDlg, PROP_POINTER);
- #else
- WORD w1, w2;
-
- w1=(WORD)GetProp(hDlg, PROP_SELECTOR);
- w2=(WORD)GetProp(hDlg, PROP_OFFSET);
-
- pEM=(PCEventMap)MAKELP(w1, w2);
- #endif
-
- switch (iMsg)
- {
- case WM_INITDIALOG:
- pEM=(PCEventMap)lParam;
-
- #ifdef WIN32
- SetProp(hDlg, PROP_POINTER, (HANDLE)pEM);
- #else
- SetProp(hDlg, PROP_SELECTOR, (HANDLE)SELECTOROF(pEM));
- SetProp(hDlg, PROP_OFFSET, (HANDLE)OFFSETOF(pEM));
- #endif
-
- /*
- * Fill the listbox with events and select the first
- * one. The selection will cause an LBN_SELCHANGE
- * notification which will set the appropriate
- * radiobutton for the action.
- */
-
- hList=GetDlgItem(hDlg, IDC_EVENTLIST);
-
- for (i=0; i < pEM->m_cEvents; i++)
- {
- //Add the name of the event to the list
- #ifdef WIN32ANSI
- char szTemp[40];
-
- WideCharToMultiByte(CP_ACP, 0
- , pEM->m_pEventMap[i].bstrName, -1, szTemp
- , 40, NULL, NULL);
- iEvent=(UINT)SendMessage(hList, LB_ADDSTRING, 0
- , (LONG)(LPSTR)szTemp);
- #else
- iEvent=(UINT)SendMessage(hList, LB_ADDSTRING, 0
- , (LONG)(LPSTR)pEM->m_pEventMap[i].bstrName);
- #endif
-
- if (LB_ERR!=iEvent)
- {
- //Give that item a pointer to the map data
- SendMessage(hList, LB_SETITEMDATA, iEvent
- , (LONG)&pEM->m_pEventMap[i]);
- }
- }
-
- //Set the initial action for the first item
- SendMessage(hList, LB_SETCURSEL, 0, 0L);
- CheckAction(hDlg, hList);
- return TRUE;
-
- case WM_COMMAND:
- hList=GetDlgItem(hDlg, IDC_EVENTLIST);
-
- switch (wID)
- {
- case IDOK:
- EndDialog(hDlg, 1);
- break;
-
- case ID_TEST:
- TestSelection(hList);
- break;
-
- case IDC_EVENTLIST:
- //Update the radiobuttons
- if (LBN_SELCHANGE==wCode)
- CheckAction(hDlg, hWndMsg);
-
- //Double-click, same as hitting Test button
- if (LBN_DBLCLK==wCode)
- TestSelection(GetDlgItem(hDlg, IDC_EVENTLIST));
- break;
-
- case IDC_BEEPNONE:
- case IDC_BEEPDEFAULT:
- case IDC_BEEPEXCLAMATION:
- case IDC_BEEPASTERISK:
- case IDC_BEEPHAND:
- case IDC_BEEPQUESTION:
- UpdateAction(hList, wID);
- break;
- }
- return TRUE;
- }
-
- return FALSE;
- }
-
-
-
- /*
- * CheckAction
- *
- * Purpose:
- * Sets the appropriate radiobutton for the current listbox
- * selection
- *
- * Parameters:
- * hDlg HWND of the dialog.
- * hList HWND of the event list.
- *
- * Return Value:
- * None
- */
-
- void CheckAction(HWND hDlg, HWND hList)
- {
- UINT i, idControl;
- PEVENTMAP pMap;
-
- i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
- pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
-
- if (LB_ERR==(LONG)pMap)
- return;
-
- //Map the action to the button
- switch (pMap->iAction)
- {
- case ACTION_NONE: idControl=IDC_BEEPNONE; break;
- case ACTION_BEEPDEFAULT: idControl=IDC_BEEPDEFAULT; break;
- case ACTION_BEEPASTERISK: idControl=IDC_BEEPASTERISK; break;
- case ACTION_BEEPEXCLAMATION: idControl=IDC_BEEPEXCLAMATION; break;
- case ACTION_BEEPHAND: idControl=IDC_BEEPHAND; break;
- case ACTION_BEEPQUESTION: idControl=IDC_BEEPQUESTION; break;
- default: idControl=IDC_BEEPNONE; break;
- }
-
- CheckRadioButton(hDlg, IDC_BEEPNONE, IDC_BEEPQUESTION, idControl);
- return;
- }
-
-
-
- /*
- * UpdateAction
- *
- * Purpose:
- * Sets the appropriate action in the event map for the
- * selected radiobutton.
- *
- * Parameters:
- * hList HWND of the event list.
- * idControl UINT identifier of the selected action control
- *
- * Return Value:
- * None
- */
-
- void UpdateAction(HWND hList, UINT idControl)
- {
- UINT i;
- PEVENTMAP pMap;
-
- i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
- pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
-
- if (LB_ERR==(LONG)pMap)
- return;
-
- //Map the button to the action
- switch (idControl)
- {
- case IDC_BEEPNONE: pMap->iAction=ACTION_NONE; break;
- case IDC_BEEPDEFAULT: pMap->iAction=ACTION_BEEPDEFAULT; break;
- case IDC_BEEPASTERISK: pMap->iAction=ACTION_BEEPASTERISK; break;
- case IDC_BEEPEXCLAMATION: pMap->iAction=ACTION_BEEPEXCLAMATION; break;
- case IDC_BEEPHAND: pMap->iAction=ACTION_BEEPHAND; break;
- case IDC_BEEPQUESTION: pMap->iAction=ACTION_BEEPQUESTION; break;
- default: pMap->iAction=ACTION_NONE; break;
- }
-
- return;
- }
-
-
-
-
-
- /*
- * TestSelection
- *
- * Purpose:
- * Executes the action associated with the currently selected
- * event.
- *
- * Parameters:
- * hList HWND of the event list.
- *
- * Return Value:
- * None
- */
-
- void TestSelection(HWND hList)
- {
- UINT i;
- PEVENTMAP pMap;
-
- i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
- pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
-
- //Event values corresond to MessageBeep values.
- if (LB_ERR!=(LONG)pMap)
- MessageBeep(pMap->iAction);
-
- return;
- }
-
-
-
-
- //CEventMap implementations
-
-
- /*
- * CEventMap::CEventMap
- * CEventMap::~CEventMap
- *
- * Parameters (Constructor):
- * pITypeInfo LPTYPEINFO from which to read event names.
- */
-
- CEventMap::CEventMap(LPTYPEINFO pITypeInfo)
- {
- m_cEvents=0;
- m_pITypeInfo=pITypeInfo;
-
- if (NULL!=m_pITypeInfo)
- m_pITypeInfo->AddRef();
-
- m_pEventMap=NULL;
- return;
- }
-
-
-
-
- CEventMap::~CEventMap(void)
- {
- if (NULL!=m_pITypeInfo)
- m_pITypeInfo->Release();
-
- if (NULL!=m_pEventMap)
- {
- UINT i;
-
- //Be sure to clean up allocated BSTRs
- for (i=0; i < m_cEvents; i++)
- SysFreeString(m_pEventMap[i].bstrName);
-
- delete [] m_pEventMap;
- }
-
- return;
- }
-
-
-
- /*
- * CEventMap::Init
- *
- * Purpose:
- * Initializes the event map with any operation prone to failure.
- * If this function fails, the caller should delete this object
- * immediately as it is unusable.
- *
- * Parameters:
- * None
- *
- * Return Value:
- * BOOL TRUE if initialization succeeded, FALSE otherwise
- */
-
- BOOL CEventMap::Init(void)
- {
- LPTYPEATTR pTA;
- UINT i;
-
- if (NULL==m_pITypeInfo)
- return FALSE;
-
- if (FAILED(m_pITypeInfo->GetTypeAttr(&pTA)))
- return FALSE;
-
- m_cEvents=pTA->cFuncs;
- m_pITypeInfo->ReleaseTypeAttr(pTA);
-
- m_pEventMap=new EVENTMAP[m_cEvents];
-
- if (NULL==m_pEventMap)
- {
- m_cEvents=0;
- return FALSE;
- }
-
- for (i=0; i < m_cEvents; i++)
- {
- LPFUNCDESC pFD;
-
- m_pEventMap[i].id=0;
- m_pEventMap[i].bstrName=NULL;
- m_pEventMap[i].iAction=ACTION_NONE;
-
- /*
- * The only piece of information we want from for each
- * event is the function name using ITypeInfo::GetNames.
- *
- * A more sophisticated container will probably save
- * more information about each event here (such as
- * parameter names and so forth) or access it dynamically
- * when the end user wants to write code for events.
- */
-
- if (SUCCEEDED(m_pITypeInfo->GetFuncDesc(i, &pFD)))
- {
- UINT cNames;
- HRESULT hr;
-
- /*
- * Since we only want the function name, we ask
- * ITypeInfo::GetNames for only one function and pass
- * the address of our one BSTR to it. If we wanted all
- * the names from ITypeInfo, then we'd allocate an
- * array of BSTRs with "new BSTR[pFD->cParams+1]"
- * and pass pFD->cParams+1 to GetNames below instead
- * of just 1. In either case, GetNames allocates
- * the string and stores the pointer to it in our
- * variable.
- */
-
- m_pEventMap[i].id=pFD->memid;
-
- hr=m_pITypeInfo->GetNames(pFD->memid
- , &m_pEventMap[i].bstrName, 1, &cNames);
-
- m_pITypeInfo->ReleaseFuncDesc(pFD);
- }
- }
-
- return TRUE;
- }
-
-
-
-
- /*
- * CEventMap::Set
- *
- * Purpose:
- * Sets the event mapping of a specific ID to a given action.
- * To clear an event, call this function with the ID and
- * ACTION_NONE.
- *
- * Parameters:
- * id DISPID of the event ID.
- * iAction EVENTACTION to assign.
- *
- * Return Value:
- * BOOL TRUE if the assignment happened, FALSE otherwise.
- */
-
- BOOL CEventMap::Set(DISPID id, EVENTACTION iAction)
- {
- BOOL fRet=FALSE;
-
- if (NULL!=m_pEventMap)
- {
- UINT i;
-
- for (i=0; i < m_cEvents; i++)
- {
- if (m_pEventMap[i].id==id)
- {
- m_pEventMap[i].iAction=iAction;
- fRet=TRUE;
- }
- }
- }
-
- return fRet;
- }
-
-
-
-
- /*
- * CEventMap::Get
- *
- * Purpose:
- * Retrieves the event assignment for a given ID.
- *
- * Parameters:
- * id DISPID of the event ID.
- *
- * Return Value:
- * EVENTACTION The action assigned to this ID. ACTION_NONE
- * if the ID is invalid.
- */
-
- EVENTACTION CEventMap::Get(DISPID id)
- {
- EVENTACTION iAction=ACTION_NONE;
-
- if (NULL!=m_pEventMap)
- {
- UINT i;
-
- //Scan the list looking for the event
- for (i=0; i < m_cEvents; i++)
- {
- if (m_pEventMap[i].id==id)
- {
- iAction=m_pEventMap[i].iAction;
- break;
- }
- }
- }
-
- return iAction;
- }
-
-
-
-
- /*
- * CEventMap::Serialize
- * CEventMap::Deserialize
- *
- * Purpose:
- * Writes or reads the mappings from DISPID to actions
- * into or from a stream.
- *
- * Parameters:
- * pIStream LPSTREAM into which to write or from which to
- * read.
- *
- * Return Value:
- * None
- */
-
- void CEventMap::Serialize(LPSTREAM pIStream)
- {
- EVENTMAP emTemp;
- ULONG cbWrite=sizeof(DISPID)+sizeof(EVENTACTION);
-
- if (NULL==pIStream)
- return;
-
- /*
- * Loop through all the IDs and write the ID and the action
- * mapping only. We don't need the event name because that
- * will be retrieved when the control is again loaded.
- *
- * Writing these pieces of info means writing the first
- * so many bytes of each EVENTMAP structure, ignoring the
- * BSTR of the name.
- */
-
- if (NULL!=m_pEventMap)
- {
- UINT i;
-
- for (i=0; i < m_cEvents; i++)
- pIStream->Write(&m_pEventMap[i], cbWrite, NULL);
- }
-
- /*
- * Finish off by writing a terminating EVENTMAP structure
- * where the action is ACTION_TAILING which only have
- * meaning here. The ID is ignored in this tail.
- */
-
- emTemp.id=0;
- emTemp.iAction=ACTION_TAILING;
- pIStream->Write(&emTemp, cbWrite, NULL);
-
- return;
- }
-
-
-
- void CEventMap::Deserialize(LPSTREAM pIStream)
- {
- if (NULL==pIStream)
- return;
-
- /*
- * When reading back the event mappings we have to be
- * careful: the control's event set might have changed
- * in the meantime so some events may no longer exist and
- * there may be new events. Therefore we read each mapping
- * one at a time (until we hit the tailing map) and find
- * the ID in the current memory event map. When we find
- * a match we update the action in memory.
- */
-
- if (NULL==m_pEventMap)
- return;
-
- while (TRUE)
- {
- ULONG cbRead=sizeof(DISPID)+sizeof(EVENTACTION);
- HRESULT hr;
- EVENTMAP em;
-
- hr=pIStream->Read(&em, cbRead, NULL);
-
- //Failure to read means a stream problem, to abort
- if (FAILED(hr))
- break;
-
- //If we hit the tail, we're done
- if (ACTION_TAILING==em.iAction)
- break;
-
- //Assign the action to the ID, if it exists
- Set(em.id, em.iAction);
- }
-
- return;
- }
-
-
-
-
-
- //Events IDispatch
-
- /*
- * CDispatchEvents::CDispatchEvents
- * CDispatchEvents::~CDispatchEvents
- *
- * Parameters (Constructor):
- * pTen PCTenant of the tenant we're in.
- */
-
- CDispatchEvents::CDispatchEvents(PCTenant pTen)
- {
- m_cRef=0;
- m_pTen=pTen;
- return;
- }
-
- CDispatchEvents::~CDispatchEvents(void)
- {
- return;
- }
-
-
-
-
- /*
- * CDispatchEvents::QueryInterface
- * CDispatchEvents::AddRef
- * CDispatchEvents::Release
- *
- * Purpose:
- * IUnknown members for CDispatchEvents object.
- */
-
- STDMETHODIMP CDispatchEvents::QueryInterface(REFIID riid, PPVOID ppv)
- {
- *ppv=NULL;
-
- if (IID_IUnknown==riid || IID_IDispatch==riid
- || m_pTen->m_iidEvents==riid)
- *ppv=this;
-
- if (NULL!=*ppv)
- {
- ((LPUNKNOWN)*ppv)->AddRef();
- return NOERROR;
- }
-
- return ResultFromScode(E_NOINTERFACE);
- }
-
- STDMETHODIMP_(ULONG) CDispatchEvents::AddRef(void)
- {
- return ++m_cRef;
- }
-
- STDMETHODIMP_(ULONG) CDispatchEvents::Release(void)
- {
- if (0!=--m_cRef)
- return m_cRef;
-
- delete this;
- return 0;
- }
-
-
-
-
-
- /*
- * CDispatchEvents::GetTypeInfoCount
- * CDispatchEvents::GetTypeInfo
- * CDispatchEvents::GetIDsOfNames
- *
- * Purpose:
- * These type-information functions are not implemented. The
- * only caller of this interface is a control which is the source
- * of the type information itself. A control will not have a
- * need to call these functions.
- *
- * Return Value:
- * HRESULT E_NOTIMPL in all cases.
- */
-
- STDMETHODIMP CDispatchEvents::GetTypeInfoCount(UINT *pctInfo)
- {
- *pctInfo=NULL;
- return ResultFromScode(E_NOTIMPL);
- }
-
- STDMETHODIMP CDispatchEvents::GetTypeInfo(UINT itinfo
- , LCID lcid, ITypeInfo **pptInfo)
- {
- *pptInfo=NULL;
- return ResultFromScode(E_NOTIMPL);
- }
-
- STDMETHODIMP CDispatchEvents::GetIDsOfNames(REFIID riid
- , OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
- {
- *rgszNames=NULL;
- *rgDispID=NULL;
- return ResultFromScode(E_NOTIMPL);
- }
-
-
-
-
- /*
- * CDispatchEvents::Invoke
- *
- * Purpose:
- * Notifies the container of the event in the control. In this
- * container we look in the event mapping for this particular
- * site and execute the appropriate action recorded in that
- * mapping. If there is no event handler set up, then nothing
- * happens.
- *
- * Parameters:
- * dispIDMember DISPID of the method or property of interest.
- * riid REFIID reserved, must be NULL.
- * lcid LCID of the locale.
- * wFlags USHORT describing the context of the invocation.
- * pDispParams DISPPARAMS * to the array of arguments.
- * pVarResult VARIANT * in which to store the result. Is
- * NULL if the caller is not interested.
- * pExcepInfo EXCEPINFO * to exception information.
- * puArgErr UINT * in which to store the index of an
- * invalid parameter if DISP_E_TYPEMISMATCH
- * is returned.
- *
- * Return Value:
- * HRESULT NOERROR or a general error code.
- */
-
-
- STDMETHODIMP CDispatchEvents::Invoke(DISPID dispIDMember, REFIID riid
- , LCID lcid, unsigned short wFlags, DISPPARAMS * pDispParams
- , VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
- {
- HRESULT hr;
- VARIANT varResult;
- EVENTACTION iAction;
- UINT i;
- PEVENTMAP pEM;
-
- ODSlu("Events IDispatch called with ID=%lu", dispIDMember);
-
- if (IID_NULL!=riid)
- return ResultFromScode(E_INVALIDARG);
-
- /*
- * We ignore lcid in this function. A multilingual application
- * might use it to determine the meaning of certain parameters
- * or perhaps as an indication of how to format data like
- * time, date, and currency or any other language or locale-
- * sensitive data.
- */
-
- /*
- * Variable handling: we don't actually do anything with any
- * of the variables from the control's events, so we don't have
- * any VARIANTARG variables to initialize.
- */
-
- /*
- * We don't handle the return value of any events if
- * events have them. We should, however, initialize an
- * empty return value just so it's not garbage.
- */
- if(NULL==pVarResult)
- pVarResult=&varResult;
-
- VariantInit(pVarResult);
- V_VT(pVarResult)=VT_EMPTY;
-
-
- //Only method calls are valid.
- if (!(DISPATCH_METHOD & wFlags))
- return ResultFromScode(DISP_E_MEMBERNOTFOUND);
-
- /*
- * Process the event by looking for dispIDMember in the
- * list maintained in the tenant that maps event IDs to
- * actions. If we find the ID, then we execute the action,
- * otherwise we do nothing.
- *
- * Control containers that allow more sophisticated programming
- * for events would do something on the same order but process
- * parameters and call user-implemented functions instead of
- * something simple like MessageBeep.
- */
-
- iAction=ACTION_NONE;
- pEM=m_pTen->m_pEventMap->m_pEventMap;
-
- for (i=0; i < m_pTen->m_pEventMap->m_cEvents; i++)
- {
- if (dispIDMember==pEM[i].id)
- {
- iAction=pEM[i].iAction;
- break;
- }
- }
-
- if (ACTION_NONE==iAction)
- hr=ResultFromScode(DISP_E_MEMBERNOTFOUND);
- else
- {
- MessageBeep((UINT)iAction);
- hr=NOERROR;
- }
-
- return hr;
- }
-