/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.ole.win32;

import java.io.*;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.ole.win32.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.widgets.*;

/**
 * OleControlSite provides a site to manage an embedded ActiveX Control within a container.
 *
 * <p>In addition to the behaviour provided by OleClientSite, this object provides the following:
 * <ul>
 *	<li>events from the ActiveX control
 * 	<li>notification of property changes from the ActiveX control
 *	<li>simplified access to well known properties of the ActiveX Control (e.g. font, background color)
 *	<li>expose ambient properties of the container to the ActiveX Control
 * </ul>
 *
 * <p>This object implements the OLE Interfaces IOleControlSite, IDispatch, and IPropertyNotifySink.
 *
 * <p>Note that although this class is a subclass of <code>Composite</code>,
 * it does not make sense to add <code>Control</code> children to it,
 * or set a layout on it.
 * </p>
 * <dl>
 *	<dt><b>Styles</b> <dd>BORDER
 *	<dt><b>Events</b> <dd>Dispose, Move, Resize
 * </dl>
 *
 * @see <a href="http://www.eclipse.org/swt/snippets/#ole">OLE and ActiveX snippets</a>
 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: OLEExample, OleWebBrowser</a>
 */
public class OleControlSite extends OleClientSite
{
	// interfaces for this container
	private COMObject iOleControlSite;
	private COMObject iDispatch;

	// supporting Property Change attributes
	private OlePropertyChangeSink olePropertyChangeSink;

	// supporting Event Sink attributes
	private OleEventSink[] oleEventSink = new OleEventSink[0];
	private GUID[] oleEventSinkGUID = new GUID[0];
	private long[] oleEventSinkIUnknown = new long[0];

	// supporting information for the Control COM object
	private CONTROLINFO currentControlInfo;
	private int[] sitePropertyIds = new int[0];
	private Variant[] sitePropertyValues = new Variant[0];

	private Font font;

	// work around for IE destroying the caret
	static int SWT_RESTORECARET;

	static final String SHELL_PROG_ID = "Shell.Explorer";	//$NON-NLS-1$

/**
 * Create an OleControlSite child widget using the OLE Document type associated with the
 * specified file.  The OLE Document type is determined either through header information in the file
 * or through a Registry entry for the file extension. Use style bits to select a particular look
 * or set of properties.
 *
 * @param parent a composite widget; must be an OleFrame
 * @param style the bitwise OR'ing of widget styles
 * @param file the file that is to be opened in this OLE Document
 *
 * @exception IllegalArgumentException
 * <ul><li>ERROR_NULL_ARGUMENT when the parent is null
 *     <li>ERROR_INVALID_ARGUMENT when the parent is not an OleFrame</ul>
 * @exception SWTException
 * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
 *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
 *     <li>ERROR_CANNOT_OPEN_FILE when failed to open file
 *     <li>ERROR_INTERFACE_NOT_FOUND when unable to create callbacks for OLE Interfaces
 *     <li>ERROR_INVALID_CLASSID
 * </ul>
 *
 * @since 3.5
 */
public OleControlSite(Composite parent, int style, File file) {
	super(parent, style, file);

	// Init site properties
	setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
	setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));
}
/**
 * Create an OleControlSite child widget using style bits
 * to select a particular look or set of properties.
 *
 * @param parent a composite widget; must be an OleFrame
 * @param style the bitwise OR'ing of widget styles
 * @param progId the unique program identifier which has been registered for this ActiveX Control;
 *               the value of the ProgID key or the value of the VersionIndependentProgID key specified
 *               in the registry for this Control (for example, the VersionIndependentProgID for
 *               Internet Explorer is Shell.Explorer)
 *
 * @exception IllegalArgumentException <ul>
 *     <li>ERROR_NULL_ARGUMENT when the parent is null
 *</ul>
 * @exception SWTException <ul>
 *     <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
 *     <li>ERROR_INVALID_CLASSID when the progId does not map to a registered CLSID
 *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
 *     <li>ERROR_CANNOT_ACCESS_CLASSFACTORY when Class Factory could not be found
 *     <li>ERROR_CANNOT_CREATE_LICENSED_OBJECT when failed to create a licensed OLE Object
 * </ul>
 */
public OleControlSite(Composite parent, int style, String progId) {
	super(parent, style);
	try {

		// check for licensing
		appClsid = getClassID(progId);
		if (appClsid == null) OLE.error(OLE.ERROR_INVALID_CLASSID);

		long licinfo = getLicenseInfo(appClsid);
		if (licinfo == 0) {

			// Open a storage object
			tempStorage = createTempStorage();

			// Create ole object with storage object
			long[] address = new long[1];
			/*
			* Bug in ICA Client 2.7. The creation of the IOleObject fails if the client
			* site is provided to OleCreate().  The fix is to detect that the program
			* id is an ICA Client and do not pass a client site to OleCreate().
			* IOleObject.SetClientSite() is called later on.
			*/
			long clientSite = isICAClient() ? 0 : iOleClientSite.getAddress();
			int result = COM.OleCreate(appClsid, COM.IIDIUnknown, COM.OLERENDER_DRAW, null, clientSite, tempStorage.getAddress(), address);
			if (result != COM.S_OK)
				OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);

			objIUnknown = new IUnknown(address[0]);

		} else {
			// Prepare the ClassFactory
			long[] ppvObject = new long[1];
			try {
				int result = COM.CoGetClassObject(appClsid, COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER, 0, COM.IIDIClassFactory2, ppvObject);
				if (result != COM.S_OK) {
					OLE.error(OLE.ERROR_CANNOT_ACCESS_CLASSFACTORY, result);
				}
				IClassFactory2 classFactory = new IClassFactory2(ppvObject[0]);
				// Create Com Object
				ppvObject = new long[1];
				result = classFactory.CreateInstanceLic(0, 0, COM.IIDIUnknown, licinfo, ppvObject);
				classFactory.Release();
				if (result != COM.S_OK)
					OLE.error(OLE.ERROR_CANNOT_CREATE_LICENSED_OBJECT, result);
			} finally {
				COM.SysFreeString(licinfo);
			}

			objIUnknown = new IUnknown(ppvObject[0]);

			// Prepare a storage medium
			ppvObject = new long[1];
			if (objIUnknown.QueryInterface(COM.IIDIPersistStorage, ppvObject) == COM.S_OK) {
				IPersistStorage persist = new IPersistStorage(ppvObject[0]);
				tempStorage = createTempStorage();
				persist.InitNew(tempStorage.getAddress());
				persist.Release();
			}
		}

		// Init sinks
		addObjectReferences();

		// Init site properties
		setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
		setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));

		if (COM.OleRun(objIUnknown.getAddress()) == OLE.S_OK) state= STATE_RUNNING;

	} catch (SWTError e) {
		dispose();
		disposeCOMInterfaces();
		throw e;
	}
}
/**
 * Create an OleClientSite child widget to edit the specified file using the specified OLE Document
 * application.  Use style bits to select a particular look or set of properties.
 * <p>
 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
 * API for <code>OleClientSite</code>. It is marked public only so that it
 * can be shared within the packages provided by SWT. It is not
 * available on all platforms, and should never be called from
 * application code.
 * </p>
 * @param parent a composite widget; must be an OleFrame
 * @param style the bitwise OR'ing of widget styles
 * @param progId the unique program identifier of am OLE Document application;
 *               the value of the ProgID key or the value of the VersionIndependentProgID key specified
 *               in the registry for the desired OLE Document (for example, the VersionIndependentProgID
 *               for Word is Word.Document)
 * @param file the file that is to be opened in this OLE Document
 *
 * @exception IllegalArgumentException
 * <ul><li>ERROR_NULL_ARGUMENT when the parent is null
 *     <li>ERROR_INVALID_ARGUMENT when the parent is not an OleFrame</ul>
 * @exception SWTException
 * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
 *     <li>ERROR_INVALID_CLASSID when the progId does not map to a registered CLSID
 *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
 *     <li>ERROR_CANNOT_OPEN_FILE when failed to open file
 * </ul>
 *
 * @noreference This method is not intended to be referenced by clients.
 *
 * @since 3.5
 */
public OleControlSite(Composite parent, int style, String progId, File file) {
	super(parent, style, progId, file);

	// Init site properties
	setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
	setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));
}
/**
 * Adds the listener to receive events.
 *
 * @param eventID the id of the event
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void addEventListener(int eventID, OleListener listener) {
	if (listener == null) OLE.error (SWT.ERROR_NULL_ARGUMENT);
	GUID riid = getDefaultEventSinkGUID(objIUnknown);
	if (riid != null) {
		addEventListener(objIUnknown.getAddress(), riid, eventID, listener);
	}

}
static GUID getDefaultEventSinkGUID(IUnknown unknown) {
	// get Event Sink I/F from IProvideClassInfo2
	long[] ppvObject = new long[1];
	if (unknown.QueryInterface(COM.IIDIProvideClassInfo2, ppvObject) == COM.S_OK) {
		IProvideClassInfo2 pci2 = new IProvideClassInfo2(ppvObject[0]);
		GUID riid = new GUID();
		int result = pci2.GetGUID(COM.GUIDKIND_DEFAULT_SOURCE_DISP_IID, riid);
		pci2.Release();
		if (result == COM.S_OK) return riid;
	}

	// get Event Sink I/F from IProvideClassInfo
	if (unknown.QueryInterface(COM.IIDIProvideClassInfo, ppvObject) == COM.S_OK) {
		IProvideClassInfo pci = new IProvideClassInfo(ppvObject[0]);
		long[] ppTI = new long[1];
		long[] ppEI = new long[1];
		int result = pci.GetClassInfo(ppTI);
		pci.Release();

		if (result == COM.S_OK && ppTI[0] != 0) {
			ITypeInfo classInfo = new ITypeInfo(ppTI[0]);
			long[] ppTypeAttr = new long[1];
			result = classInfo.GetTypeAttr(ppTypeAttr);
			if (result == COM.S_OK  && ppTypeAttr[0] != 0) {
				TYPEATTR typeAttribute = new TYPEATTR();
				COM.MoveMemory(typeAttribute, ppTypeAttr[0], TYPEATTR.sizeof);
				classInfo.ReleaseTypeAttr(ppTypeAttr[0]);
				int implMask = COM.IMPLTYPEFLAG_FDEFAULT | COM.IMPLTYPEFLAG_FSOURCE | COM.IMPLTYPEFLAG_FRESTRICTED;
				int implBits = COM.IMPLTYPEFLAG_FDEFAULT | COM.IMPLTYPEFLAG_FSOURCE;

				for (int i = 0; i < typeAttribute.cImplTypes; i++) {
					int[] pImplTypeFlags = new int[1];
					if (classInfo.GetImplTypeFlags(i, pImplTypeFlags) == COM.S_OK) {
						if ((pImplTypeFlags[0] & implMask) == implBits) {
							int[] pRefType = new int[1];
							if (classInfo.GetRefTypeOfImplType(i, pRefType) == COM.S_OK) {
								classInfo.GetRefTypeInfo(pRefType[0], ppEI);
							}
						}
					}
				}
			}
			classInfo.Release();

			if (ppEI[0] != 0) {
				ITypeInfo eventInfo = new ITypeInfo(ppEI[0]);
				ppTypeAttr = new long[1];
				result = eventInfo.GetTypeAttr(ppTypeAttr);
				GUID riid = null;
				if (result == COM.S_OK && ppTypeAttr[0] != 0) {
					riid = new GUID();
					COM.MoveMemory(riid, ppTypeAttr[0], GUID.sizeof);
					eventInfo.ReleaseTypeAttr(ppTypeAttr[0]);
				}
				eventInfo.Release();
				return riid;
			}
		}
	}
	return null;
}

/**
 * Adds the listener to receive events.
 *
 * @since 2.0
 *
 * @param automation the automation object that provides the event notification
 * @param eventID the id of the event
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *	   <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void addEventListener(OleAutomation automation, int eventID, OleListener listener) {
	if (listener == null || automation == null) OLE.error (SWT.ERROR_NULL_ARGUMENT);
	long address = automation.getAddress();
	IUnknown unknown = new IUnknown(address);
	GUID riid = getDefaultEventSinkGUID(unknown);
	if (riid != null) {
		addEventListener(address, riid, eventID, listener);
	}

}
/**
 * Adds the listener to receive events.
 *
 * @since 3.2
 *
 * @param automation the automation object that provides the event notification
 * @param eventSinkId the GUID of the event sink
 * @param eventID the id of the event
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *	   <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void addEventListener(OleAutomation automation, String eventSinkId, int eventID, OleListener listener) {
	if (listener == null || automation == null || eventSinkId == null) OLE.error (SWT.ERROR_NULL_ARGUMENT);
	long address = automation.getAddress();
	if (address == 0) return;
	char[] buffer = (eventSinkId +"\0").toCharArray();
	GUID guid = new GUID();
	if (COM.IIDFromString(buffer, guid) != COM.S_OK) return;
	addEventListener(address, guid, eventID, listener);
}

void addEventListener(long iunknown, GUID guid, int eventID, OleListener listener) {
	if (listener == null || iunknown == 0 || guid == null) OLE.error (SWT.ERROR_NULL_ARGUMENT);
	// have we connected to this kind of event sink before?
	int index = -1;
	for (int i = 0; i < oleEventSinkGUID.length; i++) {
		if (COM.IsEqualGUID(oleEventSinkGUID[i], guid)) {
			if (iunknown == oleEventSinkIUnknown[i]) {
				index = i;
				break;
			}
		}
	}
	if (index != -1) {
		oleEventSink[index].addListener(eventID, listener);
	} else {
		int oldLength = oleEventSink.length;
		OleEventSink[] newOleEventSink = new OleEventSink[oldLength + 1];
		GUID[] newOleEventSinkGUID = new GUID[oldLength + 1];
		long[] newOleEventSinkIUnknown = new long[oldLength + 1];
		System.arraycopy(oleEventSink, 0, newOleEventSink, 0, oldLength);
		System.arraycopy(oleEventSinkGUID, 0, newOleEventSinkGUID, 0, oldLength);
		System.arraycopy(oleEventSinkIUnknown, 0, newOleEventSinkIUnknown, 0, oldLength);
		oleEventSink = newOleEventSink;
		oleEventSinkGUID = newOleEventSinkGUID;
		oleEventSinkIUnknown = newOleEventSinkIUnknown;

		oleEventSink[oldLength] = new OleEventSink(this, iunknown, guid);
		oleEventSinkGUID[oldLength] = guid;
		oleEventSinkIUnknown[oldLength] = iunknown;
		oleEventSink[oldLength].AddRef();
		oleEventSink[oldLength].connect();
		oleEventSink[oldLength].addListener(eventID, listener);

	}
}
@Override
protected void addObjectReferences() {

	super.addObjectReferences();

	// Get property change notification from control
	connectPropertyChangeSink();

	// Get access to the Control object
	long[] ppvObject = new long[1];
	if (objIUnknown.QueryInterface(COM.IIDIOleControl, ppvObject) == COM.S_OK) {
		IOleControl objIOleControl = new IOleControl(ppvObject[0]);
		// ask the control for its info in case users
		// need to act on it
		currentControlInfo = new CONTROLINFO();
		objIOleControl.GetControlInfo(currentControlInfo);
		objIOleControl.Release();
	}
}
/**
 * Adds the listener to receive events.
 *
 * @param propertyID the identifier of the property
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void addPropertyListener(int propertyID, OleListener listener) {
	if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
	olePropertyChangeSink.addListener(propertyID, listener);
}

private void connectPropertyChangeSink() {
	olePropertyChangeSink = new OlePropertyChangeSink(this);
	olePropertyChangeSink.AddRef();
	olePropertyChangeSink.connect(objIUnknown);
}
@Override
protected void createCOMInterfaces () {
	super.createCOMInterfaces();

	// register each of the interfaces that this object implements
	iOleControlSite = new COMObject(new int[]{2, 0, 0, 0, 1, 1, 3, 2, 1, 0}){
		@Override
		public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
		@Override
		public long method1(long[] args) {return AddRef();}
		@Override
		public long method2(long[] args) {return Release();}
		@Override
		public long method3(long[] args) {return OnControlInfoChanged();}
		// method4 LockInPlaceActive - not implemented
		// method5 GetExtendedControl - not implemented
		// method6 TransformCoords - not implemented
		// method7 Translate Accelerator - not implemented
		@Override
		public long method8(long[] args) {return OnFocus((int)args[0]);}
		// method9 ShowPropertyFrame - not implemented
	};

	iDispatch = new COMObject(new int[]{2, 0, 0, 1, 3, 5, 8}){
		@Override
		public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
		@Override
		public long method1(long[] args) {return AddRef();}
		@Override
		public long method2(long[] args) {return Release();}
		// method3 GetTypeInfoCount - not implemented
		// method4 GetTypeInfo - not implemented
		// method5 GetIDsOfNames - not implemented
		@Override
		public long method6(long[] args) {return Invoke((int)args[0], args[1], (int)args[2], (int)args[3], args[4], args[5], args[6], args[7]);}
	};
}
private void disconnectEventSinks() {

	for (OleEventSink sink : oleEventSink) {
		sink.disconnect();
		sink.Release();
	}
	oleEventSink = new OleEventSink[0];
	oleEventSinkGUID = new GUID[0];
	oleEventSinkIUnknown = new long[0];
}
private void disconnectPropertyChangeSink() {

	if (olePropertyChangeSink != null) {
		olePropertyChangeSink.disconnect(objIUnknown);
		olePropertyChangeSink.Release();
	}
	olePropertyChangeSink = null;
}
@Override
protected void disposeCOMInterfaces() {
	super.disposeCOMInterfaces();

	if (iOleControlSite != null)
		iOleControlSite.dispose();
	iOleControlSite = null;

	if (iDispatch != null)
		iDispatch.dispose();
	iDispatch = null;
}
@Override
public Color getBackground () {

	if (objIUnknown != null) {
		// !! We are getting the OLE_COLOR - should we change this to the COLORREF value?
		OleAutomation oleObject= new OleAutomation(this);
		Variant varBackColor = oleObject.getProperty(COM.DISPID_BACKCOLOR);
		oleObject.dispose();

		if (varBackColor != null){
			int[] colorRef = new int[1];
			if (COM.OleTranslateColor(varBackColor.getInt(), 0, colorRef) == COM.S_OK)
				return Color.win32_new(getDisplay(), colorRef[0]);
		}
	}

	return super.getBackground();
}
@Override
public Font getFont () {
	if (font != null && !font.isDisposed()) return font;
	if (objIUnknown != null) {
		OleAutomation oleObject= new OleAutomation(this);
		Variant varDispFont = oleObject.getProperty(COM.DISPID_FONT);
		oleObject.dispose();

		if (varDispFont != null){
			OleAutomation iDispFont = varDispFont.getAutomation();
			Variant lfFaceName = iDispFont.getProperty(COM.DISPID_FONT_NAME);
			Variant lfHeight   = iDispFont.getProperty(COM.DISPID_FONT_SIZE);
			Variant lfItalic   = iDispFont.getProperty(COM.DISPID_FONT_ITALIC);
			//Variant lfCharSet  = iDispFont.getProperty(COM.DISPID_FONT_CHARSET);
			Variant lfBold     = iDispFont.getProperty(COM.DISPID_FONT_BOLD);
			iDispFont.dispose();

			if (lfFaceName != null &&
				lfHeight != null &&
				lfItalic != null &&
				lfBold != null){
				int style = 3 * lfBold.getInt() + 2 * lfItalic.getInt();
				font = new Font(getShell().getDisplay(), lfFaceName.getString(), lfHeight.getInt(), style);
				return font;
			}
		}
	}

	return super.getFont();
}
@Override
public Color getForeground () {

	if (objIUnknown != null) {
		// !! We are getting the OLE_COLOR - should we change this to the COLORREF value?
		OleAutomation oleObject= new OleAutomation(this);
		Variant varForeColor = oleObject.getProperty(COM.DISPID_FORECOLOR);
		oleObject.dispose();

		if (varForeColor != null){
			int[] colorRef = new int[1];
			if (COM.OleTranslateColor(varForeColor.getInt(), 0, colorRef) == COM.S_OK)
				return Color.win32_new(getDisplay(), colorRef[0]);
		}
	}

	return super.getForeground();
}
protected long getLicenseInfo(GUID clsid) {
	long[] ppvObject = new long[1];
	if (COM.CoGetClassObject(clsid, COM.CLSCTX_INPROC_HANDLER
				| COM.CLSCTX_INPROC_SERVER, 0, COM.IIDIClassFactory, ppvObject) != COM.S_OK) {
		return 0;
	}
	long result = 0;
	IUnknown unknown = new IUnknown(ppvObject[0]);
	if (unknown.QueryInterface(COM.IIDIClassFactory2, ppvObject) == COM.S_OK) {
		IClassFactory2 classFactory = new IClassFactory2(ppvObject[0]);
		LICINFO licinfo = new LICINFO();
		if (classFactory.GetLicInfo(licinfo) == COM.S_OK) {
			long[] pBstrKey = new long[1];
			if (licinfo != null && licinfo.fRuntimeKeyAvail) {
				if (classFactory.RequestLicKey(0, pBstrKey) == COM.S_OK) {
					result = pBstrKey[0];
				}
			}
		}
		classFactory.Release();
	}
	unknown.Release();
	return result;
}
/**
 *
 * Get the control site property specified by the dispIdMember, or
 * <code>null</code> if the dispId is not recognised.
 *
 * @param dispId the dispId
 *
 * @return the property value or <code>null</code>
 *
 * @since 2.1
 */
public Variant getSiteProperty(int dispId){
	for (int i = 0; i < sitePropertyIds.length; i++) {
		if (sitePropertyIds[i] == dispId) {
			return sitePropertyValues[i];
		}
	}
	return null;
}
@Override
protected int GetWindow(long phwnd) {

	if (phwnd == 0)
		return COM.E_INVALIDARG;
	if (frame == null) {
		OS.MoveMemory(phwnd, new long[] {0}, C.PTR_SIZEOF);
		return COM.E_NOTIMPL;
	}

	// Copy the Window's handle into the memory passed in
	OS.MoveMemory(phwnd, new long[] {handle}, C.PTR_SIZEOF);
	return COM.S_OK;
}
private int Invoke(int dispIdMember, long riid, int lcid, int dwFlags, long pDispParams, long pVarResult, long pExcepInfo, long pArgErr) {
	if (pVarResult == 0 || dwFlags != COM.DISPATCH_PROPERTYGET) {
		if (pExcepInfo != 0) OS.MoveMemory(pExcepInfo, new long [] {0}, C.PTR_SIZEOF);
		if (pArgErr != 0) OS.MoveMemory(pArgErr, new int[] {0}, 4);
		return COM.DISP_E_MEMBERNOTFOUND;
	}
	Variant result = getSiteProperty(dispIdMember);
	if (result != null) {
		if (pVarResult != 0) result.getData(pVarResult);
		return COM.S_OK;
	}
	switch (dispIdMember) {
			// indicate a false result
		case COM.DISPID_AMBIENT_SUPPORTSMNEMONICS :
		case COM.DISPID_AMBIENT_SHOWGRABHANDLES :
		case COM.DISPID_AMBIENT_SHOWHATCHING :
			if (pVarResult != 0) OS.MoveMemory(pVarResult, new long [] {0}, C.PTR_SIZEOF);
			if (pExcepInfo != 0) OS.MoveMemory(pExcepInfo, new long [] {0}, C.PTR_SIZEOF);
			if (pArgErr != 0) OS.MoveMemory(pArgErr, new int[] {0}, 4);
			return COM.S_FALSE;

			// not implemented
		case COM.DISPID_AMBIENT_OFFLINEIFNOTCONNECTED :
		case COM.DISPID_AMBIENT_BACKCOLOR :
		case COM.DISPID_AMBIENT_FORECOLOR :
		case COM.DISPID_AMBIENT_FONT :
		case COM.DISPID_AMBIENT_LOCALEID :
		case COM.DISPID_AMBIENT_SILENT :
		case COM.DISPID_AMBIENT_MESSAGEREFLECT :
			if (pVarResult != 0) OS.MoveMemory(pVarResult, new long [] {0}, C.PTR_SIZEOF);
			if (pExcepInfo != 0) OS.MoveMemory(pExcepInfo, new long [] {0}, C.PTR_SIZEOF);
			if (pArgErr != 0) OS.MoveMemory(pArgErr, new int[] {0}, 4);
			return COM.E_NOTIMPL;

		default :
			if (pVarResult != 0) OS.MoveMemory(pVarResult, new long [] {0}, C.PTR_SIZEOF);
			if (pExcepInfo != 0) OS.MoveMemory(pExcepInfo,new long [] {0}, C.PTR_SIZEOF);
			if (pArgErr != 0) OS.MoveMemory(pArgErr, new int[] {0}, 4);
			return COM.DISP_E_MEMBERNOTFOUND;
	}
}
private int OnControlInfoChanged() {
	long[] ppvObject = new long[1];
	if (objIUnknown.QueryInterface(COM.IIDIOleControl, ppvObject) == COM.S_OK) {
		IOleControl objIOleControl = new IOleControl(ppvObject[0]);
		// ask the control for its info in case users
		// need to act on it
		currentControlInfo = new CONTROLINFO();
		objIOleControl.GetControlInfo(currentControlInfo);
		objIOleControl.Release();
	}
	return COM.S_OK;
}
@Override
protected int OnUIDeactivate(int fUndoable) {
	return super.OnUIDeactivate(fUndoable);
}
@Override
void onFocusIn(Event e) {
	String progID = getProgramID();
	if (progID == null) return;
	if (!progID.startsWith(SHELL_PROG_ID)) {
		super.onFocusIn(e);
		return;
	}
	if (objIOleInPlaceObject == null) return;
	if (!isActivated) doVerb(OLE.OLEIVERB_UIACTIVATE);
	if (isFocusControl()) return;
	long[] phwnd = new long[1];
	objIOleInPlaceObject.GetWindow(phwnd);
	if (phwnd[0] == 0) return;
	OS.SetFocus(phwnd[0]);
}
@Override
void onFocusOut(Event e) {
	if (objIOleInPlaceObject == null) return;
	String progID = getProgramID();
	if (progID == null) return;
	if (!progID.startsWith(SHELL_PROG_ID)) {
		super.onFocusOut(e);
		return;
	}

	/*
	* FocusOut is received when focus is reassigned between handles within
	* our site.  In such cases the site should not be UIDeactivated.
	*/
	if (isFocusControl()) return;

	/*
	* Bug in Windows.  When IE7 loses focus and UIDeactivate()
	* is called, IE destroys the caret even though it is
	* no longer owned by IE.  If focus has moved to a control
	* that shows a caret then the caret disappears.  The fix
	* is to detect this case and restore the caret.
	*/
	int threadId = OS.GetCurrentThreadId();
	GUITHREADINFO lpgui1 = new GUITHREADINFO();
	lpgui1.cbSize = GUITHREADINFO.sizeof;
	OS.GetGUIThreadInfo(threadId, lpgui1);
	objIOleInPlaceObject.UIDeactivate();
	if (SWT_RESTORECARET == 0) {
		SWT_RESTORECARET = OS.RegisterWindowMessage (new TCHAR (0, "SWT_RESTORECARET", true));
	}
	if (lpgui1.hwndCaret != 0) {
		GUITHREADINFO lpgui2 = new GUITHREADINFO();
		lpgui2.cbSize = GUITHREADINFO.sizeof;
		OS.GetGUIThreadInfo(threadId, lpgui2);
		if (lpgui2.hwndCaret == 0 && lpgui1.hwndCaret == OS.GetFocus()) {
			/*
			* If the caret was not restored by SWT, put it back using
			* the information from GUITHREADINFO.  Note that this will
			* not be correct when the caret has a bitmap.  There is no
			* API to query the bitmap that the caret is using.
			*/
			if (OS.SendMessage (lpgui1.hwndCaret, SWT_RESTORECARET, 0, 0) == 0) {
				int width = lpgui1.right - lpgui1.left;
				int height = lpgui1.bottom - lpgui1.top;
				OS.CreateCaret (lpgui1.hwndCaret, 0, width, height);
				OS.SetCaretPos (lpgui1.left, lpgui1.top);
				OS.ShowCaret (lpgui1.hwndCaret);
			}
		}
	} else if (lpgui1.hwndFocus != 0 && lpgui1.hwndFocus == OS.GetFocus()) {
		OS.SendMessage (lpgui1.hwndFocus, SWT_RESTORECARET, 0, 0);
	}
}
private int OnFocus(int fGotFocus) {
	return COM.S_OK;
}
@Override
protected int QueryInterface(long riid, long ppvObject) {
	int result = super.QueryInterface(riid, ppvObject);
	if (result == COM.S_OK)
		return result;
	if (riid == 0 || ppvObject == 0)
		return COM.E_INVALIDARG;
	GUID guid = new GUID();
	COM.MoveMemory(guid, riid, GUID.sizeof);
	if (COM.IsEqualGUID(guid, COM.IIDIOleControlSite)) {
		OS.MoveMemory(ppvObject, new long[] {iOleControlSite.getAddress()}, C.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) {
		OS.MoveMemory(ppvObject, new long[] {iDispatch.getAddress()}, C.PTR_SIZEOF);
		AddRef();
		return COM.S_OK;
	}
	OS.MoveMemory(ppvObject, new long[] {0}, C.PTR_SIZEOF);
	return COM.E_NOINTERFACE;
}
@Override
protected int Release() {
	int result = super.Release();
	if (result == 0) {
		for (int i = 0; i < sitePropertyIds.length; i++) {
			sitePropertyValues[i].dispose();
		}
		sitePropertyIds = new int[0];
		sitePropertyValues = new Variant[0];
	}
	return result;
}
@Override
protected void releaseObjectInterfaces() {

	disconnectEventSinks();

	disconnectPropertyChangeSink();

	super.releaseObjectInterfaces();
}
/**
 * Removes the listener.
 *
 * @param eventID the event identifier
 *
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void removeEventListener(int eventID, OleListener listener) {
	checkWidget();
	if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);

	GUID riid = getDefaultEventSinkGUID(objIUnknown);
	if (riid != null) {
		removeEventListener(objIUnknown.getAddress(), riid, eventID, listener);
	}
}
/**
 * Removes the listener.
 *
 * @since 2.0
 * @deprecated - use OleControlSite.removeEventListener(OleAutomation, int, OleListener)
 *
 * @param automation the automation object that provides the event notification
 *
 * @param guid the identifier of the events COM interface
 *
 * @param eventID the event identifier
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
@Deprecated
public void removeEventListener(OleAutomation automation, GUID guid, int eventID, OleListener listener) {
	checkWidget();
	if (automation == null || listener == null || guid == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
	removeEventListener(automation.getAddress(), guid, eventID, listener);
}
/**
 * Removes the listener.
 *
 * @param automation the automation object that provides the event notification
 * @param eventID the event identifier
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 *
 * @since 2.0
 */
public void removeEventListener(OleAutomation automation, int eventID, OleListener listener) {
	checkWidget();
	if (automation == null || listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
	long address = automation.getAddress();
	IUnknown unknown = new IUnknown(address);
	GUID riid = getDefaultEventSinkGUID(unknown);
	if (riid != null) {
		removeEventListener(address, riid, eventID, listener);
	}
}
void removeEventListener(long iunknown, GUID guid, int eventID, OleListener listener) {
	if (listener == null || guid == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
	for (int i = 0; i < oleEventSink.length; i++) {
		if (COM.IsEqualGUID(oleEventSinkGUID[i], guid)) {
			if (iunknown == oleEventSinkIUnknown[i]) {
				oleEventSink[i].removeListener(eventID, listener);
				if (!oleEventSink[i].hasListeners()) {
					//free resources associated with event sink
					oleEventSink[i].disconnect();
					oleEventSink[i].Release();
					int oldLength = oleEventSink.length;
					if (oldLength == 1) {
						oleEventSink = new OleEventSink[0];
						oleEventSinkGUID = new GUID[0];
						oleEventSinkIUnknown = new long[0];
					} else {
						OleEventSink[] newOleEventSink = new OleEventSink[oldLength - 1];
						System.arraycopy(oleEventSink, 0, newOleEventSink, 0, i);
						System.arraycopy(oleEventSink, i + 1, newOleEventSink, i, oldLength - i - 1);
						oleEventSink = newOleEventSink;

						GUID[] newOleEventSinkGUID = new GUID[oldLength - 1];
						System.arraycopy(oleEventSinkGUID, 0, newOleEventSinkGUID, 0, i);
						System.arraycopy(oleEventSinkGUID, i + 1, newOleEventSinkGUID, i, oldLength - i - 1);
						oleEventSinkGUID = newOleEventSinkGUID;

						long[] newOleEventSinkIUnknown = new long[oldLength - 1];
						System.arraycopy(oleEventSinkIUnknown, 0, newOleEventSinkIUnknown, 0, i);
						System.arraycopy(oleEventSinkIUnknown, i + 1, newOleEventSinkIUnknown, i, oldLength - i - 1);
						oleEventSinkIUnknown = newOleEventSinkIUnknown;
					}
				}
				return;
			}
		}
	}
}
/**
 * Removes the listener.
 *
 * @param propertyID the identifier of the property
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *	    <li>ERROR_NULL_ARGUMENT when listener is null</li>
 * </ul>
 */
public void removePropertyListener(int propertyID, OleListener listener) {
	if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
	olePropertyChangeSink.removeListener(propertyID, listener);
}
@Override
public void setBackground (Color color) {

	super.setBackground(color);

	//set the background of the ActiveX Control
	if (objIUnknown != null) {
		OleAutomation oleObject= new OleAutomation(this);
		oleObject.setProperty(COM.DISPID_BACKCOLOR, new Variant(color.handle));
		oleObject.dispose();
	}
}
@Override
public void setFont (Font font) {

	super.setFont(font);

	//set the font of the ActiveX Control
	if (objIUnknown != null) {

		OleAutomation oleObject= new OleAutomation(this);
		Variant varDispFont = oleObject.getProperty(COM.DISPID_FONT);
		oleObject.dispose();

		if (varDispFont != null){
			OleAutomation iDispFont = varDispFont.getAutomation();
			FontData[] fdata = font.getFontData();
			iDispFont.setProperty(COM.DISPID_FONT_NAME,   new Variant(fdata[0].getName()));
			iDispFont.setProperty(COM.DISPID_FONT_SIZE,   new Variant(fdata[0].getHeight()));
			iDispFont.setProperty(COM.DISPID_FONT_ITALIC, new Variant(fdata[0].getStyle() & SWT.ITALIC));
			//iDispFont.setProperty(COM.DISPID_FONT_CHARSET, new Variant(fdata[0].getCharset));
			iDispFont.setProperty(COM.DISPID_FONT_BOLD,   new Variant((fdata[0].getStyle() & SWT.BOLD)));
			iDispFont.dispose();
		}
	}
	this.font = font;
	return;
}
@Override
public void setForeground (Color color) {

	super.setForeground(color);

	//set the foreground of the ActiveX Control
	if (objIUnknown != null) {
		OleAutomation oleObject= new OleAutomation(this);
		oleObject.setProperty(COM.DISPID_FORECOLOR, new Variant(color.handle));
		oleObject.dispose();
	}
}
/**
 * Sets the control site property specified by the dispIdMember to a new value.
 * The value will be disposed by the control site when it is no longer required
 * using Variant.dispose.  Passing a value of null will clear the dispId value.
 *
 * @param dispId the ID of the property as specified by the IDL of the ActiveX Control
 * @param value The new value for the property as expressed in a Variant.
 *
 * @since 2.1
 */
public void setSiteProperty(int dispId, Variant value){
	for (int i = 0; i < sitePropertyIds.length; i++) {
		if (sitePropertyIds[i] == dispId) {
			if (sitePropertyValues[i] != null) {
				sitePropertyValues[i].dispose();
			}
			if (value != null) {
				sitePropertyValues[i] = value;
			} else {
				int oldLength = sitePropertyIds.length;
				int[] newSitePropertyIds = new int[oldLength - 1];
				Variant[] newSitePropertyValues = new Variant[oldLength - 1];
				System.arraycopy(sitePropertyIds, 0, newSitePropertyIds, 0, i);
				System.arraycopy(sitePropertyIds, i + 1, newSitePropertyIds, i, oldLength - i - 1);
				System.arraycopy(sitePropertyValues, 0, newSitePropertyValues, 0, i);
				System.arraycopy(sitePropertyValues, i + 1, newSitePropertyValues, i, oldLength - i - 1);
				sitePropertyIds = newSitePropertyIds;
				sitePropertyValues = newSitePropertyValues;
			}
			return;
		}
	}
	int oldLength = sitePropertyIds.length;
	int[] newSitePropertyIds = new int[oldLength + 1];
	Variant[] newSitePropertyValues = new Variant[oldLength + 1];
	System.arraycopy(sitePropertyIds, 0, newSitePropertyIds, 0, oldLength);
	System.arraycopy(sitePropertyValues, 0, newSitePropertyValues, 0, oldLength);
	newSitePropertyIds[oldLength] = dispId;
	newSitePropertyValues[oldLength] = value;
	sitePropertyIds = newSitePropertyIds;
	sitePropertyValues = newSitePropertyValues;
}
}
