| ErdNodePainter.java |
1 /****************************************************************************
2 * This demo file is part of yFiles for Java 2.14.
3 * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4 * 72070 Tuebingen, Germany. All rights reserved.
5 *
6 * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7 * of demo files in source code or binary form, with or without
8 * modification, is not permitted.
9 *
10 * Owners of a valid software license for a yFiles for Java version that this
11 * demo is shipped with are allowed to use the demo source code as basis
12 * for their own yFiles for Java powered applications. Use of such programs is
13 * governed by the rights and conditions as set out in the yFiles for Java
14 * license agreement.
15 *
16 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19 * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 ***************************************************************************/
28 package demo.view.entityrelationship.painters;
29
30 import y.geom.YRectangle;
31 import y.layout.NodeLabelModel;
32 import y.view.GenericNodeRealizer;
33 import y.view.NodeLabel;
34 import y.view.NodeRealizer;
35 import y.view.ShapeNodePainter;
36 import y.view.YRenderingHints;
37
38 import java.awt.Color;
39 import java.awt.GradientPaint;
40 import java.awt.Graphics2D;
41 import java.awt.Paint;
42 import java.awt.Shape;
43 import java.awt.Stroke;
44 import java.awt.geom.Line2D;
45 import java.awt.geom.Rectangle2D;
46
47 /**
48 * This painter draws a node in ERD style that is used in Crow's Foot Notation.
49 *
50 * The node consists of a name label and an attributes label separated by a line
51 * and different background fillings. The painter assumes that an <code>ErdAttributesNodeLabelModel</code>
52 * is used for the attribute label.
53 * @see ErdAttributesNodeLabelModel
54 */
55 public class ErdNodePainter implements GenericNodeRealizer.ContainsTest, GenericNodeRealizer.Painter {
56 /** Shape type constant. Specifies a rectangular shape. */
57 public static final byte RECT = 0;
58
59 /** Shape type constant. Specifies a rectangular shape whose corners are rounded. */
60 public static final byte ROUND_RECT = 1;
61
62 /** Implementation for an ErdNodePainter */
63 private final ErdNodePainterImpl painter;
64
65 /** Creates a new <code>ErdNodePainter</code> with rounded corners */
66 public ErdNodePainter() {
67 this(ROUND_RECT);
68 }
69
70 /**
71 * Creates a new <code>ErdNodePainter</code>
72 * @param type The type of the rectangle
73 * @throws IllegalArgumentException If type is not <code>RECT</code> or <code>ROUND_RECT</code>
74 * @see #RECT
75 * @see #ROUND_RECT
76 */
77 public ErdNodePainter(final byte type) {
78 switch (type) {
79 case RECT:
80 case ROUND_RECT:
81 break;
82 default:
83 throw new IllegalArgumentException("Illegal type: " + type);
84 }
85
86 painter = new ErdNodePainterImpl(type);
87 }
88
89 /**
90 * Paints the ERD entity node.
91 * @param context The context node
92 * @param graphics The graphics object of the component
93 */
94 public void paint( final NodeRealizer context, final Graphics2D graphics ) {
95 painter.paint(context, graphics);
96 }
97
98 /**
99 * Paints the ERD entity node in a sloppy way.
100 * @param context The context node
101 * @param graphics The graphics object of the component
102 */
103 public void paintSloppy( final NodeRealizer context, final Graphics2D graphics ) {
104 painter.paintSloppy(context, graphics);
105 }
106
107
108 /**
109 * Checks if the coordinates <code>(x,y)</code> are within the node borders.
110 * @param context The context node
111 * @param x The x-coordinate
112 * @param y The y-coordinate
113 * @return <code>true</code> if node contains (x,y), <code>false</code> otherwise
114 */
115 public boolean contains( final NodeRealizer context, final double x, final double y ) {
116 return painter.contains(context, x, y);
117 }
118
119 /**
120 * Calculates the y-coordinate of the line that separates the name and attributes label.
121 * @param label Name label that is above the line
122 * @return y-coordinate of the separator line
123 */
124 static double separator( final NodeLabel label ) {
125 final YRectangle lr = label.getBox();
126 return lr.getY() + lr.getHeight() + Math.max(0, label.getDistance());
127 }
128
129 /**
130 * Checks if ERD style should be used.
131 * That means the background color of the default label is used to paint the name compartment.
132 * Additionally, a separator line between the name and the attributes compartment is drawn.
133 * @param context The context node
134 * @return <code>true</code> if there is a label on top of the node, <code>false</code> otherwise
135 */
136 static boolean useErdStyle( final NodeRealizer context ) {
137 if (context.labelCount() > 0) {
138 final NodeLabel nl = context.getLabel();
139 if (NodeLabel.INTERNAL == nl.getModel()) {
140 final byte p = nl.getPosition();
141 return NodeLabel.TOP == p ||
142 NodeLabel.TOP_LEFT == p ||
143 NodeLabel.TOP_RIGHT == p;
144 }
145 }
146
147 return false;
148 }
149
150
151 /**
152 * A customized {@link ShapeNodePainter} implementation for ERD nodes.
153 */
154 private static final class ErdNodePainterImpl extends ShapeNodePainter {
155
156 /**
157 * Creates a new <code>ErdNodePainterImpl</code>.
158 * @param type The type shape for the node
159 * @see #RECT
160 * @see #ROUND_RECT
161 */
162 ErdNodePainterImpl( final byte type ) {
163 super(type);
164 }
165
166 /**
167 * Paints the node shape with the two labels.
168 * @param context the node context
169 * @param graphics the graphics object
170 */
171 public void paint( final NodeRealizer context, final Graphics2D graphics ) {
172
173 // workaround for the fact that the position of the attributes label depends
174 // on the position of the name label.
175 if (context.labelCount() > 0) {
176 for (int i = 0, n = context.labelCount(); i < n; ++i) {
177 final NodeLabel nl = context.getLabel(i);
178 final NodeLabelModel model = nl.getLabelModel();
179 if (model instanceof ErdAttributesNodeLabelModel) {
180 nl.setOffsetDirty();
181 }
182 }
183 }
184
185 super.paint(context, graphics);
186 }
187
188 /**
189 * Paints the interior of the node
190 * @param context the node context
191 * @param graphics the graphics object
192 * @param shape the shape to be drawn
193 */
194 protected void paintFilledShape(
195 final NodeRealizer context,
196 final Graphics2D graphics,
197 final Shape shape
198 ) {
199 if (useErdStyle(context)) {
200 // paint in ERD style
201 final NodeLabel label = context.getLabel();
202 final double y = separator(label);
203
204 final Shape oldClip = graphics.getClip();
205
206 final Rectangle2D cb;
207 if (oldClip != null) {
208 cb = oldClip.getBounds2D();
209 } else {
210 cb = new Rectangle2D.Double(context.getX() - 10, context.getY() - 10,
211 context.getWidth() + 20, context.getHeight() + 20);
212 }
213
214 // clip area for attributes on bottom and fill it with paint
215 final Rectangle2D.Double rectangle = new Rectangle2D.Double();
216 rectangle.setFrame(
217 cb.getX(),
218 y,
219 cb.getWidth(),
220 cb.getY() + cb.getHeight() - y);
221 graphics.clip(rectangle);
222 super.paintFilledShape(context, graphics, shape);
223 graphics.setClip(oldClip);
224
225 final Color oldColor = graphics.getColor();
226
227 // clip area for entity name on top and fill it with paint
228 rectangle.setFrame(
229 cb.getX(),
230 cb.getY(),
231 cb.getWidth(),
232 y - cb.getY());
233 graphics.clip(rectangle);
234 graphics.setColor(label.getBackgroundColor());
235 graphics.fill(shape);
236
237 graphics.setColor(oldColor);
238 graphics.setClip(oldClip);
239 } else {
240 // if no ERD node, paint shape
241 super.paintFilledShape(context, graphics, shape);
242 }
243 }
244
245 /**
246 * Paints border and separator of the entity node.
247 * @param context the node realizer that this painter is associated with.
248 * @param graphics the graphics context.
249 * @param shape the shape that shall be painted.
250 */
251 protected void paintShapeBorder(
252 final NodeRealizer context,
253 final Graphics2D graphics,
254 final Shape shape
255 ) {
256 // Paint the border of the node
257 super.paintShapeBorder(context, graphics, shape);
258
259 // If ERD node, draw separator line
260 if (useErdStyle(context)) {
261 final double y = separator(context.getLabel());
262
263 // Only draw line, if separator lies within node
264 final double min = context.getY();
265 if (min < y && y < min + context.getHeight()) {
266 final double x = context.getX();
267
268 // Draw line with line type of context
269 Color lc = getLineColor(context, useSelectionStyle(context, graphics));
270 if (lc != null) {
271 Stroke oldStroke = graphics.getStroke();
272 graphics.setStroke(context.getLineType());
273 graphics.setColor(lc);
274
275 final Line2D.Double line = new Line2D.Double();
276 line.setLine(x, y, x + context.getWidth(), y);
277 graphics.draw(line);
278
279 graphics.setStroke(oldStroke);
280 }
281 }
282 }
283 }
284
285 /**
286 * Retrieves the paint to fill the interior of the node.
287 * @param context the context node
288 * @param selected whether the node is currently selected
289 * @return the current <code>Paint</code>
290 */
291 protected Paint getFillPaint(
292 final NodeRealizer context,
293 final boolean selected
294 ) {
295 // If there are two fill colors set, create a <code>GradientPaint</code>
296 final Color fc1 = getFillColor(context, selected);
297 if (fc1 != null) {
298 final Color fc2 = getFillColor2(context, selected);
299 if (fc2 != null) {
300 final double x = context.getX();
301 final double y = context.getY();
302
303 final double _y =
304 useErdStyle(context)
305 ? separator(context.getLabel())
306 : y;
307
308 return new GradientPaint(
309 (float) x,
310 (float) _y,
311 fc1,
312 (float) (x + context.getWidth()),
313 (float) (y + context.getHeight()),
314 fc2,
315 true);
316 } else {
317 return fc1;
318 }
319 } else {
320 return null;
321 }
322 }
323
324 /**
325 * Determines if the selection state should be respected while painting.
326 * @param context The context node
327 * @param gfx graphics object
328 * @return <code>true</code> if selection style is successfully applied, <code>false</code> otherwise
329 */
330 static boolean useSelectionStyle(
331 final NodeRealizer context,
332 final Graphics2D gfx
333 ) {
334 return context.isSelected() && YRenderingHints.isSelectionPaintingEnabled(gfx);
335 }
336 }
337 }
338