Implementing Custom Annotations

[Document/Medical ]

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::HitTestExt

LAnnotation::SetRestrictToContainer

LAnnotation::GetRestrictToContainer

LAnnContainer::Convert

LAnnotation::AdjustPoint

LAnnotation::Define

LAnnotation::Define2

LAnnotation::CalibrateRuler

LAnnotation::GetAutoSnapCursor

LAnnotation::GetFillModeExt

LAnnotation::GetOptions

LAnnotation::GetRotateOptions

LAnnotation::GetTextOptions

LAnnotation::SetAutoSnapCursor

LAnnotation::SetFillModeExt

LAnnotation::SetOptions

LAnnotation::SetRotateOptions

LAnnotation::SetTextOptions

In addition, functionality for adding and maintaining user-defined handles has been added to the Annotation 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.

When the object is selected, you can click and drag the object to another location by clicking on a point that is not a node.

If you click on one of the two side nodes, you can adjust the width of the rectangle, and as the rectangle is redrawn, the line in the middle adjusts so it stays in the middle.

If you click on the top or bottom node, the opposite node becomes an anchor point and you can simultaneously adjust the height and rotate the rectangle as much as 90 degrees about the anchor point in either the clockwise or counterclockwise direction. The line in the middle continuously adjusts so it remains parallel to the two sides, anchored to the top and bottom, and bisects the rectangle.

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).

Create defines for the handles, assigning each a unique ID.

(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

)

Decide on their behavior.

(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.

image\sqrblit.gif Using LAnnotation::Define functions, create the object(s) that will be displayed when drawing your annotation. An outline of the object(s) will be drawn when moving the mouse during object creation.

image\sqrblit.gif Create each of the component annotation objects using LAnnotation::CreateAnnObject (). It is useful to create a structure (EXAMPLE1) and add this to the LPCHILDDATA structure. The EXAMPLE1 structure contains fields for everything required to create the custom annotation.

image\sqrblit.gif Then call LAnnotation::Define(ANNDEFINE_BEGINSET) to begin definition of the object.

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

L_VOID Example1_LButtonDown(HWND hWnd, LPCHILDDATA pData);

)

image\sqrblit.gif Call LAnnotation::Define(ANNDEFINE_APPEND) for each of the component objects.

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

L_VOID Example1_MouseMove(LPCHILDDATA pData);

 

The Example1 object has a vertical line that is moved and resized as the rectangle grows. There is no LAnnotation::Define state to do this, so it is necessary to process the LTANNEVENT_HIGHLIGHT message. This message allows you to customize the highlighting behavior).

image\sqrblit.gif Finish defining all of the component objects (In Example1, the components are a line and a rectangle). For each of the components, set a tag that identifies component as part of a single custom object, and also identifies the part of the custom object. This is accomplished by calling AnnSetID().

image\sqrblit.gif Hide the default handles for the component objects using HideDefaultHandles() .

image\sqrblit.gif Select the component objects using LAnnotation::SetSelected , give them their default automation values (using LAnnotation::SetAutoDefaults), and finally group them together (using LAnnotation::Group). Grouping the component objects places them all in a new annotation container, which is part of the root annotation container.

Finally, add user handles to the custom object. There are several ways to add user handles. The handles can all be added to one of the component objects, or the handles can be added to one or more of the component objects. The best choice depends on the particular custom object. (Look at the function Example1_AddUserHandles to get an idea how to add user handles.)

)

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

L_VOID Example1_LButtonUp(LPCHILDDATA pData, L_UINT uTool);

)

In some cases, it may be necessary to destroy and recreate an object. (Vertical lines normally exist as independent objects and do not maintain their orientation and length with reference to other objects. These behaviors are required for the vertical line in Example1 and so the vertical line behavior must be customized. To create the illusion that the vertical line is moving you destroy the "dummy" line and recreate it where you want it—in the center of the rectangle.)

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