// FormViewer.cpp : Implementation of CFormViewer
//
// Author : David Shepherd
// Copyright (c) 2002, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "DDForms.h"
#include "FormViewer.h"
#include "FormViewerItemCollection.h"
#include "FormViewerItemDetails.h"
// number of horizontal and vertical pixels to scroll per line
#define HSCROLL_LINE_SIZE 10
#define VSCROLL_LINE_SIZE 10
// storage stream name
#define STREAM_NAME L"DaeDoeForm"
namespace AxFormViewer {
/////////////////////////////////////////////////////////////////////////////
// CItemInfo
CItemInfo::CItemInfo()
{
// initialise everything
}
CItemInfo::CItemInfo(const CItemInfo &ItemInfo)
{
ATLASSERT(FALSE); // not allowed
}
CItemInfo::~CItemInfo()
{
// clean up
if(m_HostWindow.IsWindow()) // host window
{
ATLASSERT(FALSE);
(void)m_HostWindow.DestroyWindow();
}
}
const CItemInfo &CItemInfo::operator=(const CItemInfo &ItemInfo)
{
ATLASSERT(FALSE); // not allowed
return *this;
}
CItemInfo *CItemInfo::CreateObject()
{
// create a new object
return new CItemInfo;
}
void CItemInfo::DeleteObject(const CItemInfo *pItemInfo)
{
// delete an existing object
ATLASSERT(pItemInfo!=NULL);
delete pItemInfo;
}
/////////////////////////////////////////////////////////////////////////////
// CItemInfo predicates
bool IsItemIDispatchEqual(
CItemInfo *p1,const IDispatch *p2)
{
// check the item info pointer
ATLASSERT(p1!=NULL);
// check the IDispatch pointer
ATLASSERT(p2!=NULL);
// get the items IDispatch interface
CComPtr<IDispatch> spDispatch;
(void)p1->m_HostWindow.QueryControl(&spDispatch);
// compare the interfaces
return (spDispatch==p2) ? true : false;
}
/////////////////////////////////////////////////////////////////////////////
// CFormViewer
CFormViewer::CFormViewer()
{
// initialise everything
m_bWindowOnly=TRUE;
m_pForm=NULL;
m_BorderVisible=VARIANT_TRUE;
m_BackColor=MAKE_OLE_COLOR(COLOR_BTNFACE);
}
CFormViewer::~CFormViewer()
{
// clean up
}
CItemInfo *CFormViewer::GetFocusItem()
{
// determine which item has the focus
for(CItemInfoPtrList::const_iterator iter=
m_ItemInfoPtrList.begin(); iter!=m_ItemInfoPtrList.end(); iter++)
{
CItemInfo &ItemInfo=**iter;
// if the item has the focus
CWindow FocusWindow=GetFocus();
if(ItemInfo.m_HostWindow==FocusWindow or
ItemInfo.m_HostWindow.IsChild(FocusWindow))
{
return &ItemInfo;
}
}
return NULL; // not found
}
void CFormViewer::Reset()
{
// hide the form
(void)m_pForm->ShowWindow(SW_HIDE);
// reset the form size and position
(void)m_pForm->SetWindowPos(NULL,0,0,0,0,SWP_NOZORDER);
// delete all items
CItemInfoPtrList::iterator iter_item=m_ItemInfoPtrList.begin();
while(iter_item!=m_ItemInfoPtrList.end())
{
CItemInfo &ItemInfo=**iter_item;
// remove item info from the list
iter_item=m_ItemInfoPtrList.erase(iter_item);
// destroy the host window
(void)ItemInfo.m_HostWindow.DestroyWindow();
// delete item info
CItemInfo::DeleteObject(&ItemInfo);
}
// delete all event sinks
CEventSinkPtrList::iterator iter_sink=m_EventSinkPtrList.begin();
while(iter_sink!=m_EventSinkPtrList.end())
{
CEventSink *pEventSink=*iter_sink;
// disconnect
pEventSink->Disconnect();
// delete the event sink
delete pEventSink;
// remove the event sink from the list
iter_sink=m_EventSinkPtrList.erase(iter_sink);
}
// reset the script text
m_ScriptText.clear();
// reset the script control
if(!SUCCEEDED(m_spScriptControl->Reset()))
{
throw std::exception();
}
// set the scrollbars
SetScrollbars();
}
void CFormViewer::SetScrollbars()
{
// get the available area in which to display the form
CRect Rect(0,0,0,0);
(void)GetWindowRect(Rect);
if(m_BorderVisible) // allow for the border
{
Rect.InflateRect(
-GetSystemMetrics(SM_CXEDGE),-GetSystemMetrics(SM_CYEDGE));
}
long AvailableWidth=Rect.Width();
long AvailableHeight=Rect.Height();
// get the required area in which to display the form
(void)m_pForm->GetWindowRect(Rect);
long RequiredWidth=Rect.Width();
long RequiredHeight=Rect.Height();
// determine the range of the scrollbars
DWORD HScrollRange=0;
DWORD VScrollRange=0;
for(long l=0; l<2; l++)
{
// if a horizontal scrollbar is required
if(RequiredWidth > AvailableWidth)
{
// adjust the available height
if(HScrollRange==0)
{
AvailableHeight-=GetSystemMetrics(SM_CYHSCROLL);
}
// update the horizontal scrollbar range
HScrollRange=RequiredWidth-AvailableWidth;
}
// if a vertical scrollbar is required
if(RequiredHeight > AvailableHeight)
{
// adjust the available width
if(VScrollRange==0)
{
AvailableWidth-=GetSystemMetrics(SM_CXVSCROLL);
}
// update the vertical scrollbar range
VScrollRange=RequiredHeight-AvailableHeight;
}
}
// set the range and visibility of the scrollbars
(void)SetScrollRange(SB_HORZ,0,HScrollRange);
(void)SetScrollRange(SB_VERT,0,VScrollRange);
// update the form position
BOOL Dummy=FALSE;
(void)OnHScroll(0,SB_ENDSCROLL,0,Dummy);
(void)OnVScroll(0,SB_ENDSCROLL,0,Dummy);
}
void CFormViewer::LoadScriptConstants()
{
// obtain a pointer to the script constants
HRSRC hRes=FindResource(_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDR_SCRIPT_CONSTANTS),_T("SCRIPT"));
if(hRes==NULL)
{
throw std::exception();
}
HGLOBAL hGlobal=LoadResource(_Module.GetResourceInstance(),hRes);
if(hGlobal==NULL)
{
throw std::exception();
}
LPVOID pRes=LockResource(hGlobal);
if(pRes==NULL)
{
throw std::exception();
}
// load the script constants into the script control
DWORD Size=SizeofResource(_Module.GetResourceInstance(),hRes);
if(Size==0)
{
throw std::exception();
}
if(!SUCCEEDED(m_spScriptControl->AddCode(CComBSTR(Size,(LPOLESTR)pRes))))
{
throw std::exception();
}
// windows will clean up on module termination
}
void CFormViewer::LoadExposedObjects()
{
// load the exposed objects into the script control
for(CObjectNameToIDispatchMap::const_iterator
iter=m_ExposedObjects.begin();
iter!=m_ExposedObjects.end(); iter++)
{
// object name
std::wstring Name=iter->first;
// object IDispatch interface
CComPtr<IDispatch> spDispatch=iter->second;
// add the object to the script control
if(!SUCCEEDED(m_spScriptControl->AddObject(
CComBSTR(Name.c_str()),spDispatch,VARIANT_FALSE)))
{
throw std::exception();
}
// create the event sink
CreateEventSink(Name,spDispatch);
}
}
void CFormViewer::InitialiseFormFromStream(
const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
// read the form block from the passed stream
CStreamFormBlock FormBlock;
FormBlock.ReadFromStream(spStream,StreamVersion);
// set the form dimensions
(void)m_pForm->SetWindowPos(NULL,0,0,
FormBlock.m_Size.cx,FormBlock.m_Size.cy,SWP_NOZORDER|SWP_NOMOVE);
// set the form colors
m_pForm->SetColors(FormBlock.m_BackColor,FormBlock.m_ForeColor);
// get the form IDispatch interface
CComQIPtr<IDispatch> spDispatch(m_pForm->GetUnknown());
if(spDispatch==NULL)
{
throw std::exception();
}
// add the form to the script control
if(!SUCCEEDED(m_spScriptControl->AddObject(
CComBSTR(FormBlock.m_Name.c_str()),spDispatch,VARIANT_FALSE)))
{
throw std::exception();
}
// create the event sink
CreateEventSink(FormBlock.m_Name,spDispatch.p);
}
void CFormViewer::CreateItemsFromStream(
const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
// read the item list header from the passed stream
CStreamItemListHeader ItemListHeader;
ItemListHeader.ReadFromStream(spStream,StreamVersion);
// for each item
for(long l=0; l<(long)ItemListHeader.m_Count; l++)
{
// read the item block from the passed stream
CStreamItemBlock ItemBlock;
ItemBlock.ReadFromStream(spStream,StreamVersion);
// create item info
CAutoItemInfoPtr pItemInfo(CItemInfo::CreateObject());
// set the name
pItemInfo->m_Name=ItemBlock.m_Name;
// set the tag
pItemInfo->m_Tag=ItemBlock.m_Tag;
// create the host window
if(pItemInfo->m_HostWindow.Create(*m_pForm,CRect(0,0,0,0),_T(""),
WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS)==NULL)
{
throw std::exception();
}
// get the host window IAxWinAmbientDispatch interface
CComPtr<IAxWinAmbientDispatch> spAxWinAmbientDispatch;
if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryHost(&spAxWinAmbientDispatch)))
{
throw std::exception();
}
// set the host window ambient properties
OLE_COLOR BackColor=RGB(0,0,0);
OLE_COLOR ForeColor=RGB(0,0,0);
m_pForm->GetColors(BackColor,ForeColor);
// background color
if(!SUCCEEDED(spAxWinAmbientDispatch->put_BackColor(BackColor)))
{
throw std::exception();
}
// foreground color
if(!SUCCEEDED(spAxWinAmbientDispatch->put_ForeColor(ForeColor)))
{
throw std::exception();
}
// windowless activation
if(!SUCCEEDED(spAxWinAmbientDispatch->
put_AllowWindowlessActivation(VARIANT_FALSE)))
{
throw std::exception();
}
// get the host window IAxWinHostWindow interface
CComPtr<IAxWinHostWindow> spAxWinHostWindow;
if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryHost(&spAxWinHostWindow)))
{
throw std::exception();
}
// get the item class id as text
WCHAR ClassIdText[64]=L"";
if(StringFromGUID2(ItemBlock.m_ClassId,
ClassIdText,NUM_ELEMENTS(ClassIdText,WCHAR))==0)
{
throw std::exception();
}
// for early stream versions the item data will be read directly from
// the passed stream, for later stream versions the item data will first
// be copied to a temporary data stream and then read from there
CComPtr<IStream> spDataStream=spStream;
if(ItemBlock.m_HasPersistantData and StreamVersion > 2)
{
// create the data stream
spDataStream=NULL;
if(!SUCCEEDED(CreateStreamOnHGlobal(NULL,TRUE,&spDataStream)))
{
throw std::exception();
}
// initialise the size
ULARGE_INTEGER Size;
Size.QuadPart=0;
if(!SUCCEEDED(spDataStream->SetSize(Size)))
{
throw std::exception();
}
// read the item persistant data header from the passed stream
CStreamItemPersistantDataHeader ItemPersistantDataHeader;
ItemPersistantDataHeader.ReadFromStream(spStream,StreamVersion);
// copy the item data to the data stream
if(!SUCCEEDED(spStream->CopyTo(
spDataStream,ItemPersistantDataHeader.m_Length,NULL,NULL)))
{
throw std::exception();
}
// seek back to the start of the data stream
LARGE_INTEGER Offset;
Offset.QuadPart=0;
if(!SUCCEEDED(spDataStream->Seek(Offset,STREAM_SEEK_SET,NULL)))
{
throw std::exception();
}
}
// create the item
if(!SUCCEEDED(spAxWinHostWindow->CreateControl(
ClassIdText,pItemInfo->m_HostWindow,
ItemBlock.m_HasPersistantData ? spDataStream : NULL)))
{
throw std::exception();
}
// get the item misc status
CComPtr<IOleObject> spOleObject;
if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryControl(&spOleObject)))
{
throw std::exception();
}
DWORD MiscStatus=0;
if(!SUCCEEDED(spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
{
throw std::exception();
}
// hide the item if invisible at runtime
if(MiscStatus & OLEMISC_INVISIBLEATRUNTIME)
{
(void)pItemInfo->m_HostWindow.ShowWindow(SW_HIDE);
}
// set the host window size and position
// this is required to correctly size some items
if(pItemInfo->m_HostWindow.SetWindowPos(
NULL,ItemBlock.m_Rect,SWP_NOZORDER|SWP_FRAMECHANGED)==FALSE)
{
throw std::exception();
}
// get the item IDispatch interface
CComPtr<IDispatch> spDispatch;
if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryControl(&spDispatch)))
{
throw std::exception();
}
// add the item to the script control
if(!SUCCEEDED(m_spScriptControl->AddObject(
CComBSTR(ItemBlock.m_Name.c_str()),spDispatch,VARIANT_FALSE)))
{
throw std::exception();
}
// create the event sink
CreateEventSink(ItemBlock.m_Name,spDispatch);
// add item info to the list
m_ItemInfoPtrList.push_back(pItemInfo);
// release auto resources
(void)pItemInfo.Release();
}
// reverse the item zorder
HWND InsertAfter=m_ItemRootWindow;
for(CItemInfoPtrList::reverse_iterator iter=m_ItemInfoPtrList.rbegin();
iter!=m_ItemInfoPtrList.rend(); iter++)
{
CItemInfo &ItemInfo=**iter;
// update the zorder
(void)ItemInfo.m_HostWindow.SetWindowPos(
InsertAfter,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
// insert the next item after this one
InsertAfter=ItemInfo.m_HostWindow;
}
}
void CFormViewer::LoadScriptFromStream(
const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
// read the script header from the passed stream
CStreamScriptHeader ScriptHeader;
ScriptHeader.ReadFromStream(spStream,StreamVersion);
// read in the lines
std::wstring Lines;
for(long l=0; l<(long)ScriptHeader.m_LineCount; l++)
{
std::wstring Line; // next line
ReadFromStream(spStream,Line);
Lines+=Line+L"\r\n"; // update the lines
}
// save the script text
m_ScriptText=Lines;
// load the script into the script control
if(!SUCCEEDED(m_spScriptControl->AddCode(CComBSTR(Lines.c_str()))))
{
throw std::exception();
}
}
BOOL CFormViewer::IsFormLoaded()
{
// the form is loaded if visible
return (m_pForm->GetWindowLong(GWL_STYLE) & WS_VISIBLE) ? TRUE : FALSE;
// the window style is checked rather than calling IsWindowVisible()
// since the form viewer or its parent etc may not be visible
}
void CFormViewer::InternalLoad(const CComPtr<IStream> &spStream)
{
// unload the current form
InternalUnload();
// load the script constants
LoadScriptConstants();
// load the exposed objects
LoadExposedObjects();
// read in the header
CStreamHeader StreamHeader;
StreamHeader.ReadFromStream(spStream);
// the stream version must be recognisable
if(StreamHeader.m_Version > CStreamHeader().m_Version)
{
throw std::exception();
}
// read in the form
InitialiseFormFromStream(spStream,StreamHeader.m_Version);
// read in the item list
CreateItemsFromStream(spStream,StreamHeader.m_Version);
// read in the script
LoadScriptFromStream(spStream,StreamHeader.m_Version);
// read in the footer
CStreamFooter StreamFooter;
StreamFooter.ReadFromStream(spStream);
// fire the load event
(void)m_pForm->Fire_Load();
// set the scrollbars
SetScrollbars();
// show the form
(void)m_pForm->ShowWindow(SW_SHOW);
}
void CFormViewer::InternalUnload()
{
// fire the unload event
(void)m_pForm->Fire_Unload();
// reset the form viewer
Reset();
}
void CFormViewer::ExposeObject(
const std::wstring &Name,const CComPtr<IDispatch> &spDispatch)
{
// add the object to the exposed objects map
m_ExposedObjects[Name]=spDispatch;
}
void CFormViewer::CreateEventSink(
const std::wstring &Name,const CComPtr<IDispatch> &spDispatch)
{
// create the event sink
std::auto_ptr<CEventSink> pEventSink(new CEventSink);
// connect
try { pEventSink->Connect(Name,spDispatch,this); }
catch(...)
{
// this may fail for valid reasons
return;
}
// add the event sink to the list
m_EventSinkPtrList.push_back(pEventSink.get());
(void)pEventSink.release();
}
void CFormViewer::TabForwardToNextItem()
{
// if the form contains one or more items
if(m_ItemInfoPtrList.size())
{
// find the item which has the focus
CItemInfoPtrList::const_iterator iter_focus=
std::find(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
GetFocusItem()/*will be NULL if no item has the focus*/);
// if not found
if(iter_focus==m_ItemInfoPtrList.end())
{
// start from the last item
iter_focus--;
}
// tab forward to the next item
for(long l=0; l<(long)m_ItemInfoPtrList.size(); l++)
{
// get item info for the next item
if(++iter_focus==m_ItemInfoPtrList.end())
{
iter_focus=m_ItemInfoPtrList.begin(); // wrap around
}
CItemInfo &ItemInfo=**iter_focus;
// get the item misc status
CComPtr<IOleObject> spOleObject;
if(!SUCCEEDED(
ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
{
throw std::exception();
}
DWORD MiscStatus=0;
if(!SUCCEEDED(
spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
{
throw std::exception();
}
// if the item can be tabbed to
if((MiscStatus & (
OLEMISC_ACTSLIKELABEL |
OLEMISC_INVISIBLEATRUNTIME |
OLEMISC_NOUIACTIVATE))==0)
{
// give focus to the item
(void)ItemInfo.m_HostWindow.SetFocus();
break; // done
}
}
}
}
void CFormViewer::TabBackwardToNextItem()
{
// if the form contains one or more items
if(m_ItemInfoPtrList.size())
{
// find the item which has the focus
CItemInfoPtrList::const_iterator iter_focus=
std::find(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
GetFocusItem()/*will be NULL if no item has the focus*/);
// if not found
if(iter_focus==m_ItemInfoPtrList.end())
{
// start from the first item
iter_focus=m_ItemInfoPtrList.begin();
}
// tab backward to the next item
for(long l=0; l<(long)m_ItemInfoPtrList.size(); l++)
{
// get item info for the next item
if(iter_focus==m_ItemInfoPtrList.begin())
{
iter_focus=m_ItemInfoPtrList.end(); // wrap around
}
CItemInfo &ItemInfo=**(--iter_focus);
// get the item misc status
CComPtr<IOleObject> spOleObject;
if(!SUCCEEDED(
ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
{
throw std::exception();
}
DWORD MiscStatus=0;
if(!SUCCEEDED(
spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
{
throw std::exception();
}
// if the item can be tabbed to
if((MiscStatus & (
OLEMISC_ACTSLIKELABEL |
OLEMISC_INVISIBLEATRUNTIME |
OLEMISC_NOUIACTIVATE))==0)
{
// give focus to the item
(void)ItemInfo.m_HostWindow.SetFocus();
break; // done
}
}
}
}
void CFormViewer::TabToNextItem(TabDirection Direction)
{
// tab to the next item
switch(Direction)
{
// forward
case TabDirectionForward:
TabForwardToNextItem();
break;
// backward
case TabDirectionBackward:
TabBackwardToNextItem();
break;
// unknown direction
default:
ATLASSERT(FALSE);
break;
}
}
void CFormViewer::ESNS_ProcessEvent(
const std::wstring &SourceName,const std::wstring &EventName,
SAFEARRAY *pParams,VARIANT *pResult)
{
// create the event handler name
std::wstring HandlerName=SourceName+L"_"+EventName;
// check the event handler exists in the script
// this ensures all references to objects exposed to the script control
// are released on Reset()
std::wstring ScriptText=m_ScriptText;
// remove white space
ScriptText.erase(
std::remove_if(ScriptText.begin(),ScriptText.end(),iswspace),
ScriptText.end());
// convert to lower case
std::transform(
ScriptText.begin(),ScriptText.end(),ScriptText.begin(),towlower);
// search for sub followed by the event handler name
std::wstring SearchText=L"sub"+HandlerName;
// convert to lower case
std::transform(
SearchText.begin(),SearchText.end(),SearchText.begin(),towlower);
// search
if(ScriptText.find(SearchText)!=std::wstring::npos)
{
// run the event handler
(void)m_spScriptControl->Run(
CComBSTR(HandlerName.c_str()),&pParams,pResult);
// this may fail for valid reasons
}
}
HRESULT CFormViewer::FinalConstruct()
{
IMP_BEGIN
// call the base class
HRESULT hr=CComObjectRootBase::FinalConstruct();
if(!SUCCEEDED(hr))
{
throw CHResult(hr);
}
// create the form
if(!SUCCEEDED(CComObject<CViewableForm>::CreateInstance(&m_pForm)))
{
throw CHResult(E_FAIL);
}
(void)m_pForm->AddRef();
// create the script control
if(!SUCCEEDED(m_spScriptControl.CoCreateInstance(
__uuidof(MSScriptControl::ScriptControl))))
{
throw CHResult(E_FAIL);
}
// set the language
// this is required in order for most other methods to succeed
if(!SUCCEEDED(m_spScriptControl->put_Language(CComBSTR(L"VBScript"))))
{
throw CHResult(E_FAIL);
}
// allow the script control to display a user interface
if(!SUCCEEDED(m_spScriptControl->put_AllowUI(VARIANT_TRUE)))
{
throw CHResult(E_FAIL);
}
IMP_END
return RetVal;
}
void CFormViewer::FinalRelease()
{
IMP_BEGIN
// call the base class
CComObjectRootBase::FinalRelease();
// release the form
if(m_pForm!=NULL)
{
(void)m_pForm->Release();
}
IMP_END
}
BOOL CFormViewer::PreTranslateAccelerator(LPMSG pMsg,HRESULT &hRet)
{
// this will be set TRUE if the message was translated
BOOL Handled=FALSE;
TRY
// find the item which has the focus
CItemInfo *pItemInfo=GetFocusItem();
// if found
if(pItemInfo!=NULL)
{
// get the item IOleInPlaceActiveObject interface
CComPtr<IOleInPlaceActiveObject> spOleInPlaceActiveObject;
if(!SUCCEEDED(
pItemInfo->m_HostWindow.QueryControl(&spOleInPlaceActiveObject)))
{
throw std::exception();
}
// translate the message
hRet=spOleInPlaceActiveObject->TranslateAccelerator(pMsg);
// if a translation was attempted
if(hRet!=S_FALSE)
{
// mark the message as translated
Handled=TRUE;
}
}
// if the message was not translated
if(Handled==FALSE and
// and this is a tab key down message
pMsg->message==WM_KEYDOWN and pMsg->wParam==VK_TAB)
{
// 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;
// tab to the next item
TabToNextItem(Shift ? TabDirectionBackward : TabDirectionForward);
// signal success
hRet=S_OK;
// mark the message as translated
Handled=TRUE;
}
CATCH_ALL
return Handled;
}
HRESULT CFormViewer::OnDraw(ATL_DRAWINFO &di)
{
// ATL wizard generated
IMP_BEGIN
// dc wrapper
CDCHandle dc(di.hdcDraw);
// bounding rectangle
CRect Rect=*(RECT*)di.prcBounds;
// some containers will only provide a window for us to draw on using
// this function - ie the control is not created
if(IsWindow()==FALSE) // skip if we are created
{
// draw rectangle
(void)dc.Rectangle(Rect);
// show text
LPCTSTR pszText=_T("FormViewer");
(void)dc.SetTextAlign(TA_CENTER|TA_BASELINE);
(void)dc.TextOut(
(Rect.left+Rect.right)/2,(Rect.top+Rect.bottom)/2,
pszText,lstrlen(pszText));
}
else
{
// fill in the background
COLORREF RGBBackColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_BackColor,NULL,&RGBBackColor)))
{
throw std::exception();
}
dc.FillSolidRect(Rect,RGBBackColor);
}
IMP_END
return RetVal;
}
LRESULT CFormViewer::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// set the window style
if(m_BorderVisible==VARIANT_FALSE)
{
(void)ModifyStyleEx(WS_EX_CLIENTEDGE,0);
}
// enable control containment
if(AtlAxWinInit()==FALSE)
{
throw std::exception();
}
// create the form
if(m_pForm->Create(*this,CRect(0,0,0,0),_T("ViewableForm"),
WS_CHILD|WS_CLIPCHILDREN,WS_EX_CONTROLPARENT)==NULL)
{
throw std::exception();
}
// create the item root window
m_ItemRootWindow.m_hWnd=NULL; // allow the wrapper to be reused
if(m_ItemRootWindow.Create(_T("STATIC"),
*m_pForm,CRect(0,0,0,0),_T("ItemRootWindow"),WS_CHILD|WS_DISABLED,0)==NULL)
{
throw std::exception();
}
CATCH_ALL
return Caught ? -1 : 0; // fail creation if an exception was thrown
}
LRESULT CFormViewer::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// unload the form
InternalUnload();
CATCH_ALL
return 0;
}
LRESULT CFormViewer::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// set the scrollbars
SetScrollbars();
CATCH_ALL
return 0;
}
LRESULT CFormViewer::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// to prevent flicker do not erase the background
return TRUE;
}
LRESULT CFormViewer::OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// get our client rect
CRect ClientRect(0,0,0,0);
(void)GetClientRect(ClientRect);
// get the minimum and maximum scroll positions
int MinPos=0;
int MaxPos=0;
(void)GetScrollRange(SB_HORZ,&MinPos,&MaxPos);
// get the new scroll position
long ScrollPos=GetScrollPos(SB_HORZ);
switch(LOWORD(wParam))
{
case SB_ENDSCROLL: // ignored
break;
case SB_LEFT:
ScrollPos=MinPos;
break;
case SB_RIGHT:
ScrollPos=MaxPos;
break;
case SB_LINELEFT:
ScrollPos-=HSCROLL_LINE_SIZE;
break;
case SB_LINERIGHT:
ScrollPos+=HSCROLL_LINE_SIZE;
break;
case SB_PAGELEFT:
ScrollPos-=ClientRect.Width();
break;
case SB_PAGERIGHT:
ScrollPos+=ClientRect.Width();
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
ScrollPos=HIWORD(wParam);
break;
default:
ATLASSERT(FALSE); // unexpected
break;
}
// check the new scroll position is in range
ScrollPos=min(ScrollPos,MaxPos);
ScrollPos=max(ScrollPos,MinPos);
// update the scrollbar
(void)SetScrollPos(SB_HORZ,ScrollPos);
// scroll the form horizontally
CRect FormRect(0,0,0,0);
(void)m_pForm->GetWindowRect(FormRect); // window rect
(void)ScreenToClient(FormRect); // reletive to our client
(void)m_pForm->SetWindowPos(NULL,
-ScrollPos,FormRect.top,0,0,SWP_NOZORDER|SWP_NOSIZE);
CATCH_ALL
return 0;
}
LRESULT CFormViewer::OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// get our client rect
CRect ClientRect(0,0,0,0);
(void)GetClientRect(ClientRect);
// get the minimum and maximum scroll positions
int MinPos=0;
int MaxPos=0;
(void)GetScrollRange(SB_VERT,&MinPos,&MaxPos);
// get the new scroll position
long ScrollPos=GetScrollPos(SB_VERT);
switch(LOWORD(wParam))
{
case SB_ENDSCROLL: // ignored
break;
case SB_TOP:
ScrollPos=MinPos;
break;
case SB_BOTTOM:
ScrollPos=MaxPos;
break;
case SB_LINEUP:
ScrollPos-=VSCROLL_LINE_SIZE;
break;
case SB_LINEDOWN:
ScrollPos+=VSCROLL_LINE_SIZE;
break;
case SB_PAGEUP:
ScrollPos-=ClientRect.Height();
break;
case SB_PAGEDOWN:
ScrollPos+=ClientRect.Height();
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
ScrollPos=HIWORD(wParam);
break;
default:
ATLASSERT(FALSE); // unexpected
break;
}
// check the new scroll position is in range
ScrollPos=min(ScrollPos,MaxPos);
ScrollPos=max(ScrollPos,MinPos);
// update the scrollbar
(void)SetScrollPos(SB_VERT,ScrollPos);
// scroll the form vertically
CRect FormRect(0,0,0,0);
(void)m_pForm->GetWindowRect(FormRect); // window rect
(void)ScreenToClient(FormRect); // reletive to our client
(void)m_pForm->SetWindowPos(NULL,
FormRect.left,-ScrollPos,0,0,SWP_NOZORDER|SWP_NOSIZE);
CATCH_ALL
return 0;
}
// begin standard method implementation block
#define METHOD_BEGIN \
if(IsWindow()==FALSE) \
{ \
throw CHResult(E_FAIL); \
}
// end standard method implementation block
#define METHOD_END
// begin standard property put implementation block
#define PROPPUT_BEGIN
// end standard property put implementation block
#define PROPPUT_END
// begin standard property get implementation block
#define PROPGET_BEGIN
// end standard property get implementation block
#define PROPGET_END
STDMETHODIMP CFormViewer::About()
{
// this is allowed not matter what state the viewer is in
IMP_BEGIN
// show the about box
CSimpleDialog<IDD_ABOUT_FORMVIEWER> AboutDlg;
(void)AboutDlg.DoModal();
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::LoadFromFile(BSTR FileName)
{
IMP_BEGIN
METHOD_BEGIN
// open the storage
CComPtr<IStorage> spStorage;
if(!SUCCEEDED(StgOpenStorage(BSTR2W(FileName),NULL,
STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE,
NULL,0,&spStorage)))
{
throw CHResult(E_FAIL);
}
// open the stream
CComPtr<IStream> spStream;
if(!SUCCEEDED(spStorage->OpenStream(STREAM_NAME,NULL,
STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE,
0,&spStream)))
{
throw CHResult(E_FAIL);
}
// load the form
InternalLoad(spStream);
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::Unload()
{
IMP_BEGIN
METHOD_BEGIN
// unload the form
InternalUnload();
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::Expose(BSTR Name, IDispatch *pDispatch)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
// expose the object
ExposeObject(BSTR2W(Name),pDispatch);
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::get_Form(IViewableForm **ppViewableForm)
{
IMP_BEGIN
PROPGET_BEGIN
// check parameters
if(ppViewableForm==NULL)
{
throw CHResult(E_POINTER);
}
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the form
CComQIPtr<IViewableForm> spViewableForm(m_pForm->GetUnknown());
if(spViewableForm==NULL)
{
throw CHResult(E_FAIL);
}
*ppViewableForm=spViewableForm.Detach();
PROPGET_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::get_BorderVisible(VARIANT_BOOL *pVal)
{
IMP_BEGIN
PROPGET_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// get the border visible flag
*pVal=m_BorderVisible;
PROPGET_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::put_BorderVisible(VARIANT_BOOL newVal)
{
IMP_BEGIN
PROPPUT_BEGIN
if(IsWindow()) // refresh
{
if(newVal)
{
// show the border
(void)ModifyStyleEx(0,WS_EX_CLIENTEDGE);
}
else
{
// hide the border
(void)ModifyStyleEx(WS_EX_CLIENTEDGE,0);
}
(void)SetWindowPos(NULL,0,0,0,0,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
}
// set the border visible flag
m_BorderVisible=newVal;
PROPERTY_CHANGED(DISPID_BORDERVISIBLE);
PROPPUT_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::get_BackColor(OLE_COLOR *pVal)
{
IMP_BEGIN
PROPGET_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// get the background color
*pVal=m_BackColor;
PROPGET_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::put_BackColor(OLE_COLOR newVal)
{
IMP_BEGIN
PROPPUT_BEGIN
if(IsWindow()) // refresh
{
(void)Invalidate();
}
// set the background color
m_BackColor=newVal;
PROPERTY_CHANGED(DISPID_BACKCOLOR);
PROPPUT_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::GetItemDetails(
IDispatch *pDispatch, IFormViewerItemDetails **ppFormViewerItemDetails)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(ppFormViewerItemDetails==NULL)
{
throw CHResult(E_POINTER);
}
// check the item exists
if(std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch))==
m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// create the item details
CComObject<CFormViewerItemDetails> *pItemDetails=NULL;
if(!SUCCEEDED(
CComObject<CFormViewerItemDetails>::CreateInstance(&pItemDetails)))
{
throw CHResult(E_FAIL);
}
CComPtr<IUnknown> spUnknown(pItemDetails->GetUnknown());
// set the item
pItemDetails->m_spItem=pDispatch;
// set the form viewer
CComQIPtr<IFormViewer2> spFormViewer2(GetUnknown());
if(spFormViewer2==NULL)
{
throw CHResult(E_FAIL);
}
pItemDetails->m_spFormViewer2=spFormViewer2;
// get the item details
CComQIPtr<IFormViewerItemDetails> spFormViewerItemDetails(spUnknown);
if(spFormViewerItemDetails==NULL)
{
throw CHResult(E_FAIL);
}
*ppFormViewerItemDetails=spFormViewerItemDetails.Detach();
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::GetItemClassId(IDispatch *pDispatch,BSTR *pClassId)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(pClassId==NULL)
{
throw CHResult(E_POINTER);
}
// check the item exists
CItemInfoPtrList::const_iterator iter=
std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
if(iter==m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
CItemInfo &ItemInfo=**iter;
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the item class id
CComPtr<IOleObject> spOleObject;
if(!SUCCEEDED(ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
{
throw CHResult(E_FAIL);
}
CLSID ClassId=CLSID_NULL;
if(!SUCCEEDED(spOleObject->GetUserClassID(&ClassId)))
{
throw CHResult(E_FAIL);
}
// convert to text
WCHAR ClassIdText[64]=L"";
if(StringFromGUID2(ClassId,
ClassIdText,NUM_ELEMENTS(ClassIdText,WCHAR))==0)
{
throw CHResult(E_FAIL);
}
// get the item class id
*pClassId=CComBSTR(ClassIdText).Detach();
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::GetItemName(IDispatch *pDispatch,BSTR *pName)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(pName==NULL)
{
throw CHResult(E_POINTER);
}
// check the item exists
CItemInfoPtrList::const_iterator iter=
std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
if(iter==m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
CItemInfo &ItemInfo=**iter;
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the item name
*pName=CComBSTR(ItemInfo.m_Name.c_str()).Detach();
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::GetItemTag(IDispatch *pDispatch,BSTR *pTag)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(pTag==NULL)
{
throw CHResult(E_POINTER);
}
// check the item exists
CItemInfoPtrList::const_iterator iter=
std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
if(iter==m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
CItemInfo &ItemInfo=**iter;
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the item tag
*pTag=CComBSTR(ItemInfo.m_Tag.c_str()).Detach();
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::GetItemPosition(IDispatch *pDispatch,
long *pLeft,long *pTop,long *pWidth,long *pHeight,long *pTabNumber)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(pLeft==NULL)
{
throw CHResult(E_POINTER);
}
if(pTop==NULL)
{
throw CHResult(E_POINTER);
}
if(pWidth==NULL)
{
throw CHResult(E_POINTER);
}
if(pHeight==NULL)
{
throw CHResult(E_POINTER);
}
if(pTabNumber==NULL)
{
throw CHResult(E_POINTER);
}
// check the item exists
CItemInfoPtrList::const_iterator iter=
std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
if(iter==m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
CItemInfo &ItemInfo=**iter;
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// get the item rect reletive to the screen
CRect ItemRect(0,0,0,0);
(void)ItemInfo.m_HostWindow.GetWindowRect(ItemRect);
// convert to form coordinates
(void)m_pForm->ScreenToClient(ItemRect);
// get the item position
*pLeft=ItemRect.left;
*pTop=ItemRect.top;
*pWidth=ItemRect.Width();
*pHeight=ItemRect.Height();
// the item tab number is the same as the item index but 1 based
*pTabNumber=std::distance(
(CItemInfoPtrList::const_iterator)m_ItemInfoPtrList.begin(),iter)+1;
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::SetItemPosition(IDispatch *pDispatch,
long Left,long Top,long Width,long Height,long TabNumber)
{
IMP_BEGIN
METHOD_BEGIN
// check parameters
if(pDispatch==NULL)
{
throw CHResult(E_POINTER);
}
if(Width < 0)
{
throw CHResult(E_INVALIDARG);
}
if(Height < 0)
{
throw CHResult(E_INVALIDARG);
}
// get the form rect
CRect FormRect(0,0,0,0);
(void)m_pForm->GetClientRect(FormRect);
// get the item rect
CRect ItemRect(Left,Top,Left+Width,Top+Height);
// check the item fits within the form
CRect IntersectRect(0,0,0,0);
(void)IntersectRect.IntersectRect(FormRect,ItemRect);
if(IntersectRect!=ItemRect)
{
throw CHResult(E_INVALIDARG);
}
if(TabNumber < 1 or TabNumber > (long)m_ItemInfoPtrList.size())
{
throw CHResult(E_INVALIDARG);
}
// check the item exists
CItemInfoPtrList::const_iterator iter=
std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
if(iter==m_ItemInfoPtrList.end())
{
throw CHResult(E_INVALIDARG);
}
CItemInfo &ItemInfo=**iter;
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// remove item info from the list
CItemInfoPtrList::iterator iter_delete=std::find(
m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),&ItemInfo);
ATLASSERT(iter_delete!=m_ItemInfoPtrList.end()); // should always exist
m_ItemInfoPtrList.erase(iter_delete); // remove item info
CAutoItemInfoPtr ItemInfoPtr(&ItemInfo);
// insert item info back into the list at the correct position
CItemInfoPtrList::iterator iter_insert=m_ItemInfoPtrList.begin();
std::advance(iter_insert,TabNumber-1);
m_ItemInfoPtrList.insert(iter_insert,&ItemInfo);
(void)ItemInfoPtr.Release();
// determine the window to insert the item after
HWND InsertAfter=m_ItemRootWindow;
if(TabNumber < (long)m_ItemInfoPtrList.size())
{
// advance to the next item
CItemInfoPtrList::const_iterator iter_next=m_ItemInfoPtrList.begin();
std::advance(iter_next,TabNumber);
// get the item host window
InsertAfter=(**iter_next).m_HostWindow;
}
// set the item position
(void)ItemInfo.m_HostWindow.SetWindowPos(
InsertAfter,Left,Top,Width,Height,SWP_NOACTIVATE|SWP_NOCOPYBITS);
METHOD_END
IMP_END
return RetVal;
}
STDMETHODIMP CFormViewer::get_Items(
IFormViewerItemCollection **ppFormViewerItemCollection)
{
IMP_BEGIN
PROPGET_BEGIN
// check parameters
if(ppFormViewerItemCollection==NULL)
{
throw CHResult(E_POINTER);
}
// the form should be loaded
if(IsFormLoaded()==FALSE)
{
throw CHResult(E_FAIL);
}
// create the item collection
CComObject<CFormViewerItemCollection> *pItems=NULL;
if(!SUCCEEDED(
CComObject<CFormViewerItemCollection>::CreateInstance(&pItems)))
{
throw CHResult(E_FAIL);
}
CComPtr<IUnknown> spUnknown(pItems->GetUnknown());
// add all items to the item collection
for(CItemInfoPtrList::const_iterator iter=
m_ItemInfoPtrList.begin(); iter!=m_ItemInfoPtrList.end(); iter++)
{
CItemInfo &ItemInfo=**iter;
// get the items IDispatch interface
CComPtr<IDispatch> spDispatch;
if(!SUCCEEDED(ItemInfo.m_HostWindow.QueryControl(&spDispatch)))
{
throw CHResult(E_FAIL);
}
// update the item collection
pItems->m_coll.push_back(spDispatch);
}
// get the item collection
CComQIPtr<IFormViewerItemCollection> spFormViewerItemCollection(spUnknown);
if(spFormViewerItemCollection==NULL)
{
throw CHResult(E_FAIL);
}
*ppFormViewerItemCollection=spFormViewerItemCollection.Detach();
PROPGET_END
IMP_END
return RetVal;
}
} // namespace AxFormViewer