Hi again!

If you're into creating macros or addons for CorelDRAW, I'm about to blow your mind! New to CorelDRAW X7 Update 1 is the ability to create your own tools in VBA, C# or C++. We now expose a new interface that anyone can implement -- ToolState. We've also implemented many new functions and classes that are specifically geared towards creating tools.

While you can implement new tools in any language that supports COM, in this post, I'll be focusing on C#. I'll share some VBA and C++ samples in a future post.

First, fire up your copy of Visual Studio 2012 or 2013. If you don't have a Visual Studio, fret not, you can grab a copy of Visual Studio Express here. Once you've done so, install the CorelDRAW Tool Addon extension. In Visual Studio, open up Tools->Extensions and Updates, expand the Online tab and search for "CorelDRAW". Select CorelDRAW Tool Addon and install it!

Next, create a new project (File->New Project), and navigate to Installed->Templates->Other Languages->C#, and select CorelDRAW Tool Addon from the list. Give your tool any name (I'm using "MyTool" here) and create your project.

In the menu, select Build->Build Solution (or hit F7). Congrats, you've just created your first tool. Want to try it out? Press F5 to run it. It will launch CorelDRAW and your new tool will be added to the toolbox.

                             

If you can't tell, that's a line and a gear.  Sorry, it's hard to fit anything detailed in a 16x16 image.

The sample tool has on screen preview UI, snapping, constraints, a property bar, and everything else you would expect. It is a fully fledged tool. At this point, you're probably thinking that it was too easy.

Let's crack open the code, I'll do my best to explain how it all works.

The Code

The project has two .cs files. Addon.cs contains the entry point. This class will be instantiated by CorelDRAW when you start the application, and it's here where you can register your new tool, or data source, or other tasks you'd like to perform on startup.

///<summary>
/// Constructor for the Addon.  This is called by CorelDRAW when it is discovered.
///</summary>
public Addon(Application Application)
{
	// Create and register our tool
	ToolState toolState = new CGS.MyTool(Application);
	Application.RegisterToolState("b17f7ca0-0062-4dd8-adf6-1ea2192a3d35""MyTool", toolState);
}

The function RegisterToolState tells that application that the guid "b17f7ca0-0062-4dd8-adf6-1ea2192a3d35" identifies your tool. This guid must be different for every tool (don't worry the Visual Studio extension generates a new one for every project). You can create multiple tools within your project and register them all at this spot. If you create a second tool within the project, you'll need a GUID generator. I use https://gist.github.com/ijprest/3845947 which is an AutoHotKey script, but you can find other GUID generators online (google it).

MyTool.cs contains the guts of the tool. The tool is less than 200 lines long, not too bad. MyTool inherits the ToolState interface, when CorelDRAW is using your tool, it will call into the functions that you implement. For example, when the tool starts, your OnStartState() will be executed, allowing you to set up your tool for use. When you move the mouse across the view, OnMouseMove() will be called, which allows you to update the UI, or collect mouse positions for curve creation.

The code for the sample tool is documented well, please take a few moments to read through the code and the comments to gain a basic understanding of the functions. The pseudo code for the line tool is as follows:

OnLButtonDownLeaveGrace
   save the left mouse button down position
OnMouseMove
  if the left mouse is down
    update the xor'd on screen UI with a line from the saved location to the current position
OnLButtonUp
  Create a line from the saved position to the current position. 

The UI

In your project you'll find two xslt files, AppUI.xslt and UserUI.xslt.  Xslt is an XML transformation file.  It's job is to update the users workspace to include your new tool.  You can do much more complex changes to the workspace using XSLT, but in this post I'll be limiting it to adding a new tool Icon and a new Toolbar for your tool.  If you're attempting to create any complicated UI, you may want to read up on xslt to better understand it.

AppUI.xslt is responsible for defining new items (e.g. buttons) and adding dockers or dialogs.  Anything that isn't customizable.  In the sample line tool, it adds one new item.

      <!-- C# Test Line Tool -->
      <itemData guid="b17f7ca0-0062-4dd8-adf6-1ea2192a3d35"
                type="groupedRadioButton"
                currentActiveOfGroup="*Bind(DataSource=WAppDataSource;Path=ActiveTool;BindType=TwoWay)"
                enable="*Bind(DataSource=WAppDataSource;Path=ToolboxEnable)"
                identifier="b17f7ca0-0062-4dd8-adf6-1ea2192a3d35"/>

A guid (a unique identifier) is used to identify the tool button ("b17f7ca0-0062-4dd8-adf6-1ea2192a3d35"). This guid set for both the item and the identifier.  This is also the same guid that we've registered MyTool with the application.  This is important as it causes MyTool activate when the button is clicked.

UserUI.xslt is for modifying any UI that is customizable, for example, adding buttons to existing toolbars or menus, or creating new toolbars for the tools.

  <xsl:template match="uiConfig/commandBars/commandBarData[@guid='74e03d83-404c-49f5-824a-fe0fd02ab29a']/toolbar">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <modeData guid="fd9a53f7-05cc-4cdc-b2ad-0fd6a7ea3480" captionRef="01965a34-ae38-01a0-4271-cd903b48e518">
        <item guidRef="3148e122-4f73-6e8a-4dfb-b2f6a84090d5"/>
        <item guidRef="a41164ff-c9e9-4b2c-954a-095f4204538e"/>
        <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/>
        <item guidRef="325d7c86-da3a-4610-bd25-5cf98cf66a41"/>
        <item guidRef="42bef211-16e5-4b4f-b9ee-52b7b5476232"/>
        <item guidRef="d289f32b-3808-4510-b892-fd2cb0820209"/>
        <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/>
        <item guidRef="8723ad52-3e31-473c-8756-7ae85abcc483"/>
        <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/>
        <item guidRef="e6644135-9dab-4935-8ab9-fc85527810ca"/>
        <item guidRef="6ae897fd-2eab-4dad-b172-f4fb768c273e"/>
        <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/>
        <item guidRef="97e5c8b9-f7e2-453c-92a2-af7fb61e67b2"/>
        <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/>
        <item guidRef="76dd9e0c-e9c2-42b6-b8bb-f7717482164e"/>
        <item guidRef="6bd00383-d132-4686-8a21-8d7052b6a81b"/>
        <item guidRef="40cc3adc-498a-4256-a98a-d1210a4019f7"/>
      </modeData>
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="uiConfig/commandBars/commandBarData[@guid='7c905e2a-cb64-4ba1-aff0-c306f392680c']/toolbar/item[@guidRef='af052244-1121-4a82-b0d2-194106db742a']">
    <xsl:copy-of select="."/>
    <item guidRef="b17f7ca0-0062-4dd8-adf6-1ea2192a3d35"/>
  </xsl:template>

The first section defines a new property bar mode for the tool property bar.  The guid "fd9a53f7-05cc-4cdc-b2ad-0fd6a7ea3480" is unique for this tool as well, and in MyTool::StartAtState(), you'll find the following line:

currentAttribs.PropertyBarGuid = "fd9a53f7-05cc-4cdc-b2ad-0fd6a7ea3480";

This tells CorelDRAW that we want to use this property bar when the tool starts. The items on this property bar come from the application, mostly from the real line tool.  I'll describe how to create your own items in a future post.

The second section adds the item to the toolbox.  The code specifically places the new tool after the Smart Fill tool in the tool box.

The Resources

The three types of resources that we can expose to CorelDRAW are icons, cursors and strings.  config.xml ties a guid to a particular resource.  In this case we tie "b17f7ca0-0062-4dd8-adf6-1ea2192a3d35" to string "10223" and icon "104.  You can find these resources in your project by double-clicking on resources.rct.

A note on adding icons or cursors: I find it easiest to create the icon in an external editor and then use "Add Resource", "Icon", Import, instead of using the built-in editor.

The APIs 

Below is a list of all of the new functions added to CorelDRAW's object model in Update 1.  Have fun playing with them!
 

  • Application:
    • ActiveToolStateGuid [get,set]
      • This is a replacement for ActiveTool.  It uses a GUID to identify the tool.  Unlike ActiveTool, it can be used to get or set any state. 
    • RegisterToolState, UnregisterToolState
      • This allows third parties to register or unregister custom tools with CorelDRAW.  UnregisterToolState does not need to be called before shutdown.  RegisterToolState can be called multiple times with the same tool (e.g. if the code has been updated)
    • CreateOnScreenCurve, CreateOnScreenHandle, CreateOnScreenText
      • Helper function to create an on screen curve, handle or text that is xor'd onto the view
    • Math
      • Object that provides math utility functions
    • RegisterUserApplicationPreference
      • This allows third parties to register custom application preferences.  Application preferences persist after shutdown and are stored with the workspace.  They can be access via the AppPrefMgr datasource.  They can also be bound to UI within the application.  They support most basic types (int, string, double, bool) and Color
    • GetApplicationPreferenceValue, SetApplicationPreferenceValue
      • Get or set any application preference
  • Document:
    • CreateCurveFitToPoints, CreateCurveFitToPointsAndCusps
      • This function creates a Curve that is fit to a Points array.
    • SampleColorAtPoint, SampleColorInArea
      • This function retrieves a color at a given point (or area) in the document
  • Curve:
    • AppendSubpathFitToPoints, AppendSubpathFitToPointsAndCusps
      • This appends a new curve subpath that is fit to a Points array.
    • ApplyTransformMatrix
      • This method transforms the curve by a TransformMatrix
    • AppendSubpathFromPoints
      • Creates line segments from the points array
    • CreateCurveMappedToStroke
      • Creates a curve that is this curve mapped to a stroke, similar to artistic media
    • CreateCurveMappedToStrokeAndReferenceLine
      • Creates a curve that is this curve mapped to a stroke and reference line, similar to artistic media
    • AutoReduceNodes
      • Reduces the number of nodes in a curve based on a tolerance value
    • JoinTouchingSubpaths
      • Connects any subpaths that touch
    • AppendSubpathCircle
      • Creates a circle subpath
    • AppendSubpathRectangle
      • Creates a rectangle subpath
    • AppendSubpathThreePointArc
      • Creates a 3-point arc subpath
    • SelfWeldClosedSubpaths
      • Self welds any closed subpaths
  • Window:
    • ScreenDistanceToDocumentDistance
      • Converts a screen distance in pixels to document unit distance
      • This used to be in Math
    • DocumentDistanceToScreenDistance
      • Converts document unit distance to a screen distance in pixels
  • Node:
    • GetPoint, SetPoint
      • Get or set a position of a node using a Point
  • Segment:
    • GetPerpendicularVectorAt
      • Get a perpendicular vector at a position on the segment
    • GetTangentVectorAt
      • Get a tangent vector at a position on the segment
    • GetPointAt
      • Get a point from a position on the segment
  • Subpath:
    • EqualDivide
      • Divide the subpath into equal length subpaths
    • GetEvenlySpacedPoints
      • Get a PointRange of points evenly spaced along the subpath
    • GetPerpendicularVectorAt
      • Get a perpendicular vector at a position on the subpath
    • GetTangentVectorAt
      • Get a tangent vector at a position on the subpath
    • GetPointAt
      • Get a point from a position on the subpath
  • Shape:
    • TransformationMatrix [get, set]
      • Gets or sets the transformation applied to the shape.  This is a replacement for GetMatrix, SetMatrix which do not package up the matrix in an object.
    • ApplyTransformMatrix
      • This method transforms the shape by a TransformMatrix
  • Color:  --- Color has be modified to correctly work while holding color styles.
    • IsColorStyle
      • Determines if the color is a color style.
    • ColorStyleName
      • Returns the name of the color style if the color is a color style, otherwise empty.
    • ModifyColorStyleColor
      • If this color is a color style, this function modifies the underlying color style (i.e. all colors in the document referencing the color style will change).  If changeWholeHarmony is true, all colors in the same harmony will change similar to the wheel in the color styles docker.  If the color is a rule-based harmony, the whole harmony always changes, but the saturation only changes if changeWholeHarmony is true.
  • StyleSheet:
    • AllColorStyles
      • Gets an array of all color styles in the document
    • CreateColorStyle
      • Creates a new color style
    • DeleteAllColorStyles
      • Deletes all color styles in the document
    • DeleteColorStyle
      • Deletes a specific color style in the document
    • RenameColorStyle
      • Renames a color style in the document
  • OnScreenCurve: [NEW!]  -- This object represents an xor'd curve that is drawn within the view
    • Show, Hide
      • Show/Hide the curve
    • SetPen, SetNoPen
      • set the width and color of the curve
    • SetBrush, SetNoBrush
      • set the fill of the curve
    • SetCurve, SetLine, SetRectangle, SetCircle, SetPoints
      • Set the curve
  • OnScreenHandle: [NEW!] -- This object represents an xor'd handle glyph that is drawn within the view
    • Show, Hide
      • Show/Hide the handle
    • SetHandleColor
      • Sets the color of the handle
    • SetPosition
      • Sets the position of the handle
    • UpdateHotTracking
      • The handle can be draw larger when the mouse is over it, this function allows for this.
    • IsHotTracked
      • Determines if the handle is hottracking
    • IsOnHandle
      • Determines is a given point is over the handle
  • OnScreenText: [NEW!] -- This object represents an xor'd text that is drawn within the view
    • Show, Hide
      • Show/Hide the text
    • SetTextColor
      • Set the text color
    • SetTextAndPosition
      • Set the text and position of the text
    • SetText
      • Set only the text
    • SetPixelOffset
      • Set the the amount in pixels to offset the text by
    • UpdatePosition
      • Update only the position of the text
  • ToolStateAttributes: [NEW!] -- This object provides several attributes and helper functions for custom tools
    • PropertyBarGuid [get, set]
      • Get/Set the guid for the tool's property bar
    • ContextMenuGuid [get, set]
      • Get/Set the guid for the tool's context menu
    • UseTabletPressure [get, set]
      • Determines if the tools should be collecting pressure data
    • AllowTempPickState [get, set]
      • Determines if the tool can resize objects using the resize handles
    • AllowAutopan [get, set]
      • Determines if the tool allows autopan while dragging
    • AllowContextMenu [get, set]
      • Determines if the tool should pop up a context menu with the right mouse click
    • CanUpdateSelectionOnMouseClick [get, set]
      • Determines if the tool can select different objects when a click happens
    • DeselectOnLButtonDown [get, set]
      • Determines if the tool should de-select the current shape when the left mouse is clicked
    • EnterGraceStateOnLButtonDown [get, set]
      • Determines if the tool should enter a grace state when the left mouse button is clicked. This is useful to determine if a click or click+drag is happening.
    • StatusInfo
      • Sets the status bar tool string
    • SetCursor
      • Sets the current cursor
    • SetCursorGuid
      • Sets the current cursor using a resource GUID (which can be exposed via the config.xml file of an addon)
    • StartTimer
      • Starts a timer that calls into the tool
    • StopTimer
      • Stops a timer
    • SnapMouse
      • Snaps a mouse position using current snapping settings
    • AnchoredSnapMouse
      • Snaps a mouse position using current snapping settings, and allows for more advanced snapping (such as snapping a line to a perpendicular)
    • ConstrainMouse
      • Uses the current constrain settings to constrain to an angle
    • IsKeyDown
      • Determines if a given key is held
    • CurrentPressure
      • Returns the current pen pressure between 0 and 1.
  • ToolState: [NEW!] -- This is an object that must be implemented and registered to create custom tools
    • OnStartState
      • callback when the tool is activated
    • OnExitState
      • callback when the tool is deactivated
    • OnMouseMove
      • callback when the mouse is moved
    • OnLButtonDown
      • callback when the left mouse button is pressed
    • OnLButtonDownLeaveGrace
      • callback when the left mouse button is pressed and a small amount of time has passed or the mouse has been moved a short distance
    • OnLButtonUp
      • callback when the left mouse button is released
    • OnLButtonDblClick
      • callback when the left mouse button is double clicked
    • OnClick
      • callback when the mouse has been clicked
      • New parameter, no longer returns boolean
    • OnRButtonDown
      • callback when the right mouse button has been pressed
    • OnRButtonUp
      • callback when the right mouse button has been released
    • OnKeyDown
      • callback when a key is pressed
    • OnKeyUp
      • callback when a key is released
    • OnDelete
      • callback when the Delete key is pressed and released
    • OnAbort
      • callback when the ESC key is pressed and released
    • OnCommit
      • callback when IsDrawing is true and the user presses enter or space
    • OnSnapMouse
      • callback to handle snapping
    • OnTimer
      • callback when a registered timer event is invoked
    • IsDrawing
      • callback to tell the tool that we're drawing. Several small behaviour changes happen when we are drawing, for example, autopan will activate when the user moves the mouse close to the edge of the view.
  • Point: [NEW!] -- object to wrap a simple x and y position
    • x, y [get, set]
      • the coordinate
    • Add, Subtract
      • Shift the x and y by a vector offset
    • DistanceTo
      • Get the distance to another point
    • GetCopy
      • Get a copy of the point
  • PointRange: [New!] -- object that hold multiple points
    • Item [get, set]
      • Get or set a point within the array
    • First, Last
      • Get the first or last point within the array
    • Count
      • Get the number of points within the array
    • AddPoint, AddPointXY
      • Add a point to the end of the array
    • InsertPoint, AddPoints, InsertPoints
      • Add and insert points into the array
    • Remove
      • Remove a point from the array
    • RemoveRange
      • Remove several points in the array
    • RemoveAll
      • Clear the array
    • RemoveAdjacentDuplicates
      • Removes a point where the next point is the same
    • Reverse
      • Reverses the order of the points
    • Smoothen
      • Run a smoothing operation of the points
      • Renamed
    • GetCopy
      • Get a copy of the PointRange
  • Vector: [New!] -- object to wrap a simple x and y vector
    • x, y [get, set]
      • The vector's x and y offset
    • Length [get, set]
      • Get or set the length of the vector
    • Angle [get, set]
      • Get or set the angle of the vector
    • GetOffsettedPoint
      • Given a point and a distance, creates a new point which is offsetted perpendicualr to the vector
    • Add, Subtract
      • Add or subtract vectors
    • MultiplyBy
      • Scale the vector
    • Negate
      • Negate the vector
    • Normalize
      • Set the vector's length to 1.0
    • AngleBetween
      • Gets the angle in degrees between the vector and another in counter clockwise direction
    • SmallAngleBetween
      • Gets the small angle in degrees between the vector and another
    • DotProduct
      • Computes the dot product between this vector and another
    • CrossProduct
      • Computes the cross product between this vector and another
    • SetFromPoints
      • Creates a vector from point A to B
    • ProjectOnto
      • Projects this vector onto another
    • GetCopy
      • Get a copy of the vector
  • TransformMatrix: [New!] -- object that stores a linear transformation matrix
    • d11, d12, d21, d22 [get, set]
      • the raw data that stores the transmations - please prefer not to use these
    • TranslationX, TranslationY [get, set]
      • gets or sets the translation of the matrix
    • BasisXAxis, BasisYAxis [get, set]
      • gets or sets the basis vectors for the matrix
    • Translation [get, set]
      • gets or sets the translation for the matrix
    • SetToIdentity
      • set the matrix to the identity
    • Invert
      • inverts the matrix
    • TranslateBy, TranslateByVector, SetTranslation
      • translates the matrix
    • Rotate
      • rotates the matrix around (0, 0)
    • RotateAround
      • rotates the matrix around a given point
    • Scale
      • scales a matrix around (0, 0)
    • ScaleAround
      • scales a matrix around a given point
    • Transform
      • transforms a matrix by another
    • TransformAround
      • transforms a matrix by another around a given point
    • TransformPoint, UntransformPoint
      • transforms a point
    • TransformPoints, UntransformPoints
      • transforms an array of points
    • TransformVector, UntransformVector
      • transforms a vector
    • IsIdentity
      • determine if the matrix is the identity matrix
    • IsSkewedOrRotatedOrMirrored
      • determine if the matrix is the skewed or rotated or mirrored
    • ContainsOnlyTranslation
      • determines if the matrix is only translations
    • IsSkewedOrRotated
      • determines if the matrix is skewed or rotated
    • IsScaledOrSkewedOrRotated
      • determines if the matrix is scaled or skewed or rotated
    • IsOrthogonal
      • determines if the matrix is orthagonal
    • IsOrthonormalAxisAligned
      • determines if the matrix is ortho normal axis aligned
    • IsOrthonormal
      • determines if the matrix is orthonormal
    • IsMirrored
      • determines if the matrix is mirrored
    • IsScaled
      • determines if the matrix is scaled
    • IsTranslated
      • determines if the matrix is translated
    • GetCopy
      • Get a copy of the TransformMatrix
  • MathUtils: [NEW!] -- object provided by the application that provides several utility math functions
    • Interpolate
      • Creates a point that interpolates two given points
    • IntersectLineSegments
      • Gets the intersection point of two line segments - returns false if the lines don't intersect
    • DistanceToLineSegment
      • Gets the distance of a point to a line segment
    • ClosestPointToLineSegment
      • Gets the closest point to a line segment
    • IntersectInfiniteLines
      • Gets the intersection point of two lines - returns false if the lines are parallel
    • DistanceToInfiniteLine
      • Gets the distance of a point to a line
    • ClosestPointToInfiniteLine
      • Gets the closest point to a line
    • GetRandomReal
      • Gets a random double precision number
    • GetRandomInteger
      • Gets a random integer
    • FitLineToPoints
      • Fits a line to an array of points
    • MidPoint
      • Creates a point that bisects two given points
    • CreatePoint
      • Creates a point
    • CreateVector
      • Creates a vector
    • CreatePointRange
      • Creates a PointRange
    • CreateIdentityTransformMatrix
      • Creates an identity TransformMatrix
    • CreateRotationTransformMatrix
      • Creates a rotation TransformMatrix
    • CreateTranslationTransformMatrix
      • Creates a translation TransformMatrix
    • CreateScaleTransformMatrix
      • Creates a scale TransformMatrix
    • CreateLineSegmentTransformMatrix
      • Creates a TransformMatrix which when applied to the line (FromStart, FromEnd) will result in the line (ToStart, ToEnd)
  • Things to look out for

    • VBA tools will require delay load to be off, for this reason alone, I recommend you stick to C# or C++. You can also write your tool as a VB dll if that's the language you're most comfortable in.
    • If you change UserUI.xslt, you must reset your workspace by holding F8. I recommend you work in a dummy workspace while you develop your tool.  You can change AppUI.xslt as often as you like, it is loaded every time.
    • You must be signed in with a valid CorelDRAW Standard or Premium membership to use a custom addon tool.  Standard membership is free for all customers who have purchased the CorelDRAW Graphics Suite X7.
    • if you have both 32 and 64-bit versions of CorelDRAW installed, the Visual Studio project template will select the 64-bit version.

    For more information on creating custom tools, please see: http://community.coreldraw.com/blogs/insider/archive/2014/06/06/tutorial-make-a-zig-zag-tool-for-coreldraw-x7-update-1.aspx

    • Your list of utilities looks impressive. I would especially be interested in a replacement for CorelDraw's GetMatrix and SetMatrix routines, which don't behave in a way I can figure out. In particular, I can't get the tx and ty parameters (which are not explained in the help files, but presumably are for translation of a shape) to work as expected.

      I followed your instructions and installed Visual Studio. However, a seach failed to show anything related to CorelDraw.

      Is your Addon not available any more?

      Best regards, Holger Nielsen

    • How to make it work on Corel Designer? (it doesn't work for me even if I add the Designer.addon in my pluggin folder)

    • Building a WPF menu in the toolbar, and when floating, it should be an event, but fixed to the top toolbar event is not responding. I don't know why?