Congree API for Proxy Editor

What is the Proxy Editor?

The Congree Authoring Client does not use the editor API explicitly. It uses the abstraction intermediary layer (internal Congree Client API). When it comes to the implementation of this abstraction layer, each editor uses its own version. Proxy Editor is one of them. It hides all the difficulties and nuances that have to be considered when implementing the internal Congree Client API (see Fig.1). Thus, the Proxy Editor is not a content editor and does not have its own UI view. But at the same time, the Congree Authoring Client interacts with the Proxy Editor as a full-fledged editor.


Fig.1 Сomponents interaction scheme

As shown in Fig. 1, the Plain Text Editor is a build-in UI control to be used by an application uses somehow. Congree does not interact directly with this control. The application itself interacts with this control and informs the Proxy Editor about all changes.

When do you need to use the Proxy Editor?

The Proxy Editor is required if the ISV application uses a content editor which does not have a Congree integration (such as f.e. EditControl ). The Proxy Editor makes it possible to integrate Congree into the application directly.

The Proxy Editor provides its own API. Using this API, the ISV application can, through the Proxy Editor, check any content. Congree will assume that it is working with a real editor.

How to integrate the Proxy Editor into an application?

In order to integrate the Proxy Editor into the application, Congree API for ISV applications should be used. Please read this article carefully before proceeding. 

The Proxy Editor provides its own implementation of the ICongreeBootstrapper interface. The implementation ICongreeBootstrapper has a co-class CongreeBootstrapperComShim.

[
	uuid(6D01C672-7B61-45D3-826A-43D072EEEEB5)
]
co-class CongreeBootstrapperComShim 
{
	interface ICongreeBootstrapper;
};

The ProgId for the co-class CongreeBootstrapperComShim is "Congree.Bootstrapper.ComShim"

After creating and initializing the Congree bootstrapper, application should create the control context (method CreateControlContext  of ICongreeBootstrapper). This method accepts the VARIANT control argument. This argument is an instance of the Proxy Editor.

How to create an instance of the Proxy Editor?

IProxyEditorFactory is used to create the Proxy Editor. The application can obtain IProxyEditorFactory via QueryInterface method (see https://docs.microsoft.com/en-us/cpp/atl/queryinterface?view=vs-2019) of the ICongreeBootstrapper instance.

IProxyEditorFactory

IProxyEditorFactory is a factory of Proxy Editors. IProxyEditorFactory can create a different version of the Proxy Editor. Currently one version is used, but further other versions of the Proxy Editor may appear. Each version of Proxy Editor is an implementation of the IProxyEditor interface.

[
	odl,
	uuid(762A13A2-009E-4645-96AB-27DF5285A98B),
	version(1.0),
	dual,
	oleautomation
]
interface IProxyEditorFactory
{
	[id(0x00000001)]
	HRESULT CreateDefaultProxyEditor([out, retval] IProxyEditor** pRetVal);
};

Methods

CreateDefaultProxyEditor

Creates the instance of the IDefaultPlainTextProxyEditor.


IProxyEditor

The basic interface from which all Proxy Editors are inherited.

[
	odl,
	uuid(4D2D1935-94BA-48BF-B64B-970E6099BE38),
	version(1.0),
	dual,
	oleautomation
]
interface IProxyEditor
{
};

IDefaultPlainTextProxyEditor

IDefaultPlainTextProxyEditor provides a proxy editor API which is designed to work with simple text editors (such as, e.g., typical EditControl), which have a plain text as content.

[
	uuid(3033EE5E-5D9D-4B6C-AB77-6E2CA0FF3B2A),
	version(1.0)
]
co-class DefaultProxyEditor 
{
	interface IDefaultPlainTextProxyEditor;
	[default, source] dispinterface IDefaultPlainTextProxyEditorEvents;
};
 
[
odl,
uuid(6CA19D1C-8D69-4E21-B515-482700D9DF51),
version(1.0),
dual,
oleautomation
]
interface IDefaultPlainTextProxyEditor 
{
	[id(0x00000001)]
	HRESULT CheckContent([in] BSTR content);

	[id(0x00000002)]
	HRESULT CursorPositionChanged([in] long position);
};

Methods

CheckContent

Passes a text content to the Proxy Editor. 

Remark: This method should be called every time when the content of the Editor control is changed.

CursorPositionChanged

Informs Congree in which position the cursor is in the text. The position can take values from 0 to the maximum content length - 1.

Remark: When the user moves the cursor in the Editor control, the application should notify the Proxy Editor where the cursor is. In this case, Congree will highlight the corresponding result in the Congree panels.


Remarks:

There are situations when an application should not call CheckContent immediately after the content has changed, but after a while. For example, when the user is typing. It is more optimal to send the content once when user stopped typing.

In the provided c# sample we use the 1 second timer. The content is sent when the user stops typing and does not change the content longer than 1 sec.


The content can contain several paragraphs which should be split by LF (https://en.wikipedia.org/wiki/Newline). 


IDefaultPlainTextProxyEditorEvents

In order to receive feedback from Congree, the Proxy Editor provides the IDefaultPlainTextProxyEditorEvents interface. The application can subscribe to these events.

[
uuid(78BD9CB3-7E5B-49A9-9ACF-D88F00FFF5C0),
version(1.0)
]
dispinterface IDefaultPlainTextProxyEditorEvents 
{
properties:
methods:
	[id(0x60020000)]
	void OnReplaceText(
					[in] long startPosition, 
					[in] long length, 
					[in] BSTR newText);
	[id(0x60020001)]
	void OnSelectText(
					[in] long startPosition, 
					[in] long length);
};

Methods

OnReplaceText

Occurs when the user selects a result in some Congree panel. The event handler obtains information about which part of the text should be highlighted in the Editor control (start position and length).

OnSelectText 

Occurs when the user applies some result in the Congree panel (e.g. spelling mistake). The event handler obtains information about which part of the text should be replaced in the Editor control. It provides the start position, length, and new text.


Note: Try not to call CheckContent inside the handler of OnReplaceText.


This example shows how to initialize and use the IProxyEditor (c# example).

// create the Proxy Editor bootstrapper
var congreeDomain = new CongreeBootstrapperComShim();
 
// query interface the ICongreeBootstrapper
ICongreeBootstrapper congreeBootstrapper = (ICongreeBootstrapper)congreeDomain;
congreeBootstrapper.Initialize(null);
 
// query interface the IProxyEditorFactory
IProxyEditorFactory proxyEditorFactory = (IProxyEditorFactory)congreeBootstrapper;
 
// create default proxy editor
IDefaultPlainTextProxyEditor defaultPlainTextProxyEditor = proxyEditorFactory.CreateDefaultPlainTextProxyEditor();
 
// query interface IDefaultPlainTextProxyEditorEvents
IDefaultPlainTextProxyEditorEventsdefaultPlainTextProxyEditorEvents = (IDefaultPlainTextProxyEditorEvents)defaultPlainTextProxyEditor;
 
// subscribe to events
defaultPlainTextProxyEditorEvents.OnSelectText += SelectText;
defaultPlainTextProxyEditorEvents.OnReplaceText += ReplaceText;
 
// create the CreateControlContext method and pass the proxy editor as argument
// Note: the collection of placeholders should be already created (to get more details read the "Congree API for ISV applications" article). 
IControlContext controlContext = congreeBootstrapper.CreateControlContext(defaultPlainTextProxyEditor, placeholders);
 
//Here we have a working cycle of the application. Whenever the user changes the content in the Editor control, the application should call CheckContent of Proxy Editor.
defaultPlainTextProxyEditor.CheckContent(content);
 
// if the application receives the SelectText event, the corresponding text fragment should be selected in the Editor control.
// if the application receives the ReplaceText event,the corresponding text fragment should be replaced in the Editor control. 
 
// if control context is not necessary anymore (e.g. the document is closed) we should close the context to release resources
controlContext.Close();
 
....
// here we are not going to communicate with Congree anymore, bootstrapper can be destroyed
congreeBootstrapper.Destroy(); 

C# Sample