This picture shows the most important classes of Spring++ and their relations:
Graphics code operates on basic primitives such as points, sizes, vectors and rectangles:
Spring++ uses the Microsoft GDI+ graphics library (https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-gdi-start) that contains a rich set of drawing components. Spring++ uses only a subset of those:
The DiagramElement class is the base class of all diagram elements. Each element has a bounding rectangle and a name. All elements can measure their dimensions, draw themselves to the graphics surface, clone themselves, return their attributes as XML and parse the attributes back from XML:
Layout contain subdimensions, colors, fonts and paddings of diagram elements. The unit of measure of all layout elements is millimeter. The layout is saved as XML to the layout.xml file located in the layout subdirectory of the springpp directory.
This picture contains the layout classes:
The LayoutElement class is the base class of all layout elements. Each layout element has a name, can return its attributes as XML and parse attributes back from XML.
The fundamental layout elements contain color, font and padding attributes:
The container layout elements contain the line widths, colors, fonts and paddings of the class, object and note diagram elements:
The rest layout elements contains the widths, heights, radii, colors, fonts and paddings of the rest of diagram elements:
The Connector class represents an ID of an endpoint of a container class. Each container object has twelve endpoints, three on each side of the container object. In addition a class element has one endpoint for each operation and attribute, and an object element has one endpoint for each attribute.
The primary connector point specifies the side of the container element: left, top, right, or bottom, or it can be operation or attribute. The secondary connector point specifies one of the three points: left and right sides have top, center and bottom points, and the top and bottom sides have left, center and right points. If the primary point is operation or attribute the secondary point is the index of the corresponding operation or attribute in a container element.
The EndPoint class represents an endpoint of a relationship element. It has a reference to a diagram element, that is a container element, an operation element or an attribute element. It has also a connector, a calculated canvas point, two text elements and an index of a container element in a diagram. The primary text element contains the text drawn above the relationship line and the secondary text element contains the text drawn below the relationship line.
IndexList is a class template that is specialized for the DiagramElement, AttributeElement or OperationElement. The IndexList owns its elements. The elements are referenced by their index in the list.
The ContainerElement class contains references to relationships that the container element is part of. When the location or size of the container element changes its Measure method will be called. It calculates new source and target points for the relationships.
The ClassElement class contains an isAbstract flag that tells whether the class is abstract or not. When the isAbstract flag is changed, the rep member is also changed to a new instance of ConcreteClassElement or AbstractClassElement.
The measuring and drawing of the class element is delegated to the representation class. The GetClassLayout method of the representation returns an instance of the ConcreteClassLayoutElement or AbstractClassLayoutElement depending whether the representation is concrete or abstract. The font style of the caption in the concrete layout is bold and in the abstract layout it is bold italic.
The ObjectElement class is simpler than the class element. The DrawFrame method draws a rounded rectangle frame to the object element.
The NoteElement class has a vector of text lines. The Draw method draws a frame with a note corner bitmap and calls the graphics.DrawString() method to draw the text lines.
Instances of the AttributeElement class are contained by object or class elements. Instances of the OperationElement class are contained by class elements. Attributes and operations have a reference to their container element and they may be part of a relationship.
The RelationshipElement class represents a relationship between two container elements. It has rkind member that is the kind of relationship represented, cardinality that holds the cardinality of the relationship, source and target endpoints and a vector of routing points.
When the rkind member changes, the SetRep method is called to create a new instance of relationship representation and set it as the value of the rep member.
The Measure method calculates the sizes of primary and secondary text elements for the source and target endpoints, and then calls the SetTextLocations method to set new locations for those text elements.
The Inheritance class is an example of a concrete relationship. The Draw method draws a line from the starting point of the inheritance arrow to the first routing point of the inheritance. Then it draws the line from the previous routing point to the next routing point in turn. Finally it calculates the points of the lines that consist the target end of the inheritance arrow and draws the lines.
For the rest of the concrete releationships, the implementations are analogous.
The TextElement represents text drawn above or below a relationship line segment either near to the source or target endpoint of the relationship. If the tkind is primaryText the text is drawn above the line segment, and if it is secondaryText the text is drawn below the line segment.
The SetSourceLocation and SetTargetLocation methods calculate the location of the text element for the source and target endpoints respectively.
As an example of the location calculation code for the SetPrimarySourceLocation is shown. In this case the line parameter is the starting line segment of the relationship line. First the line segment is converted to a vector u. Then the code branches depending on the quarter of the circle the vector u points to. The code is shown when the angle of the vector is between 0° and 90°. Spring++ follows a common convention of graphics systems that the x axis points to the right and the y axis points down. Thus the x_unit and y_unit vectors are set accordingly. The variable h is set as the height of the bounding rectangle of the text element.
The code is based on vector mathematics and geometry, specifically properties of similar right triangles.
The calculations for other quarters of the circle are analogous but differ slightly.
To support undo and redo, user actions are captured as Commands and saved to a CommandList. The command list is owned by the Diagram class.
The CommandList class holds commands and keeps track of current command index.
The AddCommand method calls the Finalize method of the command and saves the command to the current index position, or adds the command to the end of the command list, if the current index is equal to the number of commands in the list. Then the current index is incremented and the list is resized to hold exactly current index number of commands.
The CanUndo method returns true if the current command index is positive and false otherwise.
The Undo method decrements the current command index and then calls the Undo method of the command at the current command index.
The CanRedo method returns true if the current command index is less then the number of commands in the list and false otherwise.
The Redo method calls the Redo method of the command at the current command index, and increments the current command index.
The CanUndo and CanRedo methods are used to enable and disable the Undo and Redo menu items respectively.
There are three kind of selections derived from the Selection base class: the EmptySelection is the default when nothing is selected, the ElementSelection contains indeces of diagram elements selected by control+clicking them, and the RectangleSelection is created by dragging a rectangle around diagram elements.
Actions are added to a context menu when user right-clicks either an individual diagram element or a selection.
Actions derived from the DiagramElementAction are added to a context menu by the AddActions method of a diagram element when it is right-clicked. When user selects a menu item from the context menu, the Execute method of a diagram element action gets called.
Actions derived from the DiagramAction are added to a context menu by the AddActions method of a selection when it is right-clicked. When user selects a menu item from the context menu, the Execute method of a diagram action gets called.