Implementing Custom Annotations

Implementing Custom Annotations

Functionality being added with Version 14 now makes it possible for you to create custom annotations that look and behave in almost any way imaginable. Custom annotations can be made to respond to almost any key sequence: the annotation DLL sends the WM_LTANNEVENT messages LTANNEVENT_LBUTTONDOWN, LTANNEVENT-MOUSEMOVE, and the LTANNEVENT_LBUTTONUP; and the WM_LTANNEVENT IParam points to an ANNMOUSEPOS structure that contains detailed information about the mouse’s position and status.

 

The following functions are used when creating custom annotations:

LAnnContainer::RestrictCursor

LAnnContainer::HitTest

LAnnotation::SetRestrictToContainer

LAnnotation::GetRestrictToContainer

LAnnContainer::Convert

LAnnotation::AdjustPoint

LAnnotation::Define

LAnnotation::Define2

LAnnotation::CalibrateRuler

LAnnotation::GetAutoSnapCursor

LAnnotation::GetFillMode

LAnnotation::GetOptions

LAnnotation::GetRotateOptions

LAnnotation::GetTextOptions

LAnnotation::SetAutoSnapCursor

LAnnotation::SetFillMode

LAnnotation::SetOptions

LAnnotation::SetRotateOptions

LAnnotation::SetTextOptions

In addition, functionality for adding and maintaining user-defined handles has been added to the Annotation C++ Class Library. This functionality has been implemented by adding the AddUserHandle, GetUserHandle, GetUserHandles, ChangeUserHandle, DeleteUserHandle, EnumerateHandles, and EnumHandleCallBack functions to each of the following classes:

LAnnAudioClip

LAnnButton

LAnnCrossProduct

LAnnCurve

LAnnCurveClosed

LAnnEllipse

LAnnEncrypt

LAnnFreehand

LAnnFreehandHotSpot

LAnnHilite

LAnnHotSpot

LAnnLine

LAnnNote

LAnnPolygon

LAnnPoint

LAnnPolyline

LAnnPolyRuler

LAnnProtractor

LAnnPointer

LAnnPushPin

LAnnRedact

LAnnRectangle

LAnnRTF

LAnnRuler

LAnnStamp

LAnnText

LAnnTextPointer

LAnnToolBar

LAnnVideo

In addition, several messages have been added to the WM_LTANNEVENT event in order to implement the functionality for creating custom annotations.

There are 5 steps involved in creating a custom annotation:

1.

Break the custom annotation into components.

2.

Decide on the location and behavior of the annotation object handles.

3.

Define the functions for creating the annotation.

4.

Define the functions for the user handles.

5.

Define any Miscellaneous functions that may be necessary.

For more information, refer to the WM_LTANNEVENT event and ANNMOUSEPOS structure documentation.

The following narrative describes the process of creating a custom annotation.

In the LEAD toolkit, there is no LAnnotation::Define capability for moving and resizing a line simultaneously. The following example creates a custom object that demonstrates this capability. More specifically, this custom object creates a rectangle that has a line in the middle of it, and that line continues to stay in the middle whenever you resize and/or rotate the rectangle as shown in the figure below. For compelete sample code, refer to Example1 in your C:\Program Files\LEAD Technologies, Inc\LEADTOOLS\Examples\DLL\ANNOTATE directory.

 

This object being created has 4 nodes. The line bisecting the rectangle has a yellow node at the top end and a gray node at the bottom end. The two sides have a node in the middle: a black node on the left and a red node on the right.

To create this object, perform the following steps:

1.

Break the custom annotation into components. At this point, it is easiest to make a copy of example1.h and example1.c, and replace each occurrence of "example1" with the name of your custom annotation object.

 

(The custom annotation object Example1 consists of a rectangle and a square. We will create the custom object by grouping together a line and a rectangle.)

2.

Decide on the location and behavior of the annotation object handles (Example1 has four handles—one on each side, one on the top, and one on the bottom).

(The defines for Example1 are:

#define HANDLE_ID_HORIZONTAL_TOP 100
#define HANDLE_ID_HORIZONTAL_BOTTOM 101
#define HANDLE_ID_VERTICAL_LEFT 102
#define HANDLE_ID_VERTICAL_RIGHT 103

)

(The behavior for the handles for Example1 are:

HANDLE_ID_VERTICAL_LEFT --horizontal resize 

HANDLE_ID_VERTICAL_RIGHT --horizontal resize 

HANDLE_ID_HORIZONTAL_TOP -- rotate around ANDLE_ID_HORIZONTAL_BOTTOM 

HANDLE_ID_HORIZONTAL_BOTTOM -- vertical resize 

)

3.

Define the functions for creating the annotation. Create functions are called (via the WM_LBUTTONDOWN message) when creating the custom annotation using automation and the annotation menu.

(For illustration of this in Example1, refer to the code beginning with:

L_VOID Example1_LButtonDown(HWND hWnd, LPCHILDDATA pData);

)

(For illustration of this in Example1, refer to the code beginning with:

L_VOID Example1_MouseMove(LPCHILDDATA pData);

 

)

(For illustration of this in Example1, refer to the code beginning with:

L_VOID Example1_LButtonUp(LPCHILDDATA pData, L_UINT uTool);

)

4.

Define functions for the user handles. ** Update Handle functions are called (via the LTANNEVENT_LBUTTONDOWN message) when the mouse drags a user handle.

 

In Example1 there are three functions, and these completely define the behavior of each user handle. Each of these functions does the following:

1.

Calls AnnGetNeighborObjects--The user handle can be on any of the component annotation objects. AnnGetNeighborObjects gets an array of ALL the corresponding objects that make up the custom annotation.

2.

Calls AnnSortNeighborObjects--This sorts the component objects according to the component number. Recall that when you create the component objects,

AnnSetID(HANNOBJECT hObject, L_INT nMainID, L_INT nPartID) was called for each component. 

The first component has a nPartID of 0, the next has nPartID of 1, and so on. After sorting the objects, pData->AnnObjectNeighbors[n] now contains the nth component of the custom object.

3.

Does a switch statement on (pData->nUserHandleID) to determine which user handle is being dragged, and uses the corresponding LAnnotation::Define and LAnnotation::Define2 statements to accomplish the desired behavior.

4.

Restricts the mouse cursor. This can easily be done in two ways:

 

i) Calling LAnnContainer::RestrictCursor(pData->hContainer, &rcClip, NULL, NULL, FALSE);

 

(See the function BoxSide custom object for an example.) 

 

Or

 

ii) Setting pMousePos->pt (in the pANNMOUSEPOS structure) to the adjusted point. 

 

Set pMousePos->fUpdatePos = TRUE. The function LAnnotation::AdjustPoint can be helpful, especially when working with rotated objects. For details, see the documentation for WM_LTANNEVENT.

 

(For illustration of the first needed function in Example1, refer to the code beginning with:

 

L_VOID Example1_Handle_LButtonDown(LPCHILDDATA pData, pANNMOUSEPOS pMousePos); 

 

If dragging handles HANDLE_ID_VERTICAL_LEFT or HANDLE_ID_VERTICAL_RIGHT, we want to resize the rectangle (LAnnotation::Define(ANNDEFINE_BEGINRESIZE) and move the line (LAnnotation::Define (ANNDEFINE_MOVE).

 

If dragging handle HANDLE_ID_HORIZONTAL_BOTTOM, we want to again resize the rectangle (LAnnotation::Define(ANNDEFINE_BEGINRESIZE)), but with the line instead of moving it, we now want to resize it. (LAnnotation::Define(ANNDEFINE_BEGINRESIZE)).

 

If dragging handle HANDLE_ID_HORIZONTAL_TOP, we want to rotate the rectangle around HANDLE_ID_HORIZONTAL_BOTTOM. This is accomplished by setting HANDLE_ID_HORIZONTAL_BOTTOM to be the anchor point, and doing an LAnnotation::Define(ANNDEFINE_BEGINROTATE). This is done for both the line and the rectangle.

 

)

5.

Define any Miscellaneous functions that may be necessary. The example custom annotation object needs two miscellaneous functions: one that creates and sets the location of all the user handles, and another that is called whenever any component object is about to be highlighted.

See Also

WM_LTANNEVENT Message

ANNMOUSEPOS

Implementing Annotations

Automated User Interface for Annotations