1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.flowchart.painters;
15  
16  import y.view.AbstractCustomNodePainter;
17  import y.view.NodeRealizer;
18  import y.view.GenericNodeRealizer;
19  import y.view.Graph2D;
20  import y.base.Node;
21  import y.view.YRenderingHints;
22  
23  import java.awt.Graphics2D;
24  import java.awt.Color;
25  import java.awt.BasicStroke;
26  import java.awt.GradientPaint;
27  import java.awt.geom.GeneralPath;
28  import java.awt.geom.Point2D;
29  import java.awt.geom.Rectangle2D;
30  
31  /**
32   * This class is an implementation of {@link GenericNodeRealizer.Painter} that draws the annotation symbol of flowchart diagrams.
33   */
34  public class FlowchartAnnotationPainter extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest, FlowchartRealizerConstants {
35    private GeneralPath shape;
36    private Rectangle2D outline;
37  
38  
39    public FlowchartAnnotationPainter() {
40      outline = new Rectangle2D.Double();
41      shape = new GeneralPath();
42    }
43  
44    /**
45     * Paints a flowchart annotation symbol.
46     * @param context the context node
47     * @param graphics the graphics context to use
48     * @param sloppy whether to draw the node sloppily
49     */
50    protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
51      updateShape(context);
52      updateOutline(context);
53  
54      final boolean useSelectionStyle = useSelectionStyle(context, graphics);
55      graphics.setStroke(getLineStroke(context, useSelectionStyle));
56  
57      Color fillColor1 = getFillColor(context, useSelectionStyle);
58      Color fillColor2 = getFillColor2(context, useSelectionStyle);
59      Color lineColor = getLineColor(context, useSelectionStyle);
60  
61      if (fillColor1 != null) {
62        if (fillColor2 != null && useGradientStyle(graphics)) {
63          double x = context.getX();
64          double y = context.getY();
65          double width = context.getWidth();
66          double height = context.getHeight();
67          GradientPaint gp = new GradientPaint((float) x, (float) y, fillColor1, (float) x + (float) width,
68              (float) y + (float) height, fillColor2);
69          graphics.setPaint(gp);
70        } else {
71          graphics.setColor(fillColor1);
72        }
73        graphics.fill(outline);
74  
75        if (lineColor != null) {
76          graphics.setColor(lineColor);
77          graphics.setStroke(new BasicStroke(2));
78          graphics.draw(shape);
79        }
80      }
81    }
82  
83    static boolean useGradientStyle( final Graphics2D graphics ) {
84      return YRenderingHints.isGradientPaintingEnabled(graphics);
85    }
86  
87    static boolean useSelectionStyle( final NodeRealizer context, final Graphics2D gfx ) {
88      return context.isSelected() && YRenderingHints.isSelectionPaintingEnabled(gfx);
89    }
90  
91    /**
92     * Calculates the annotation outline for the specified node.
93     * @param context The node context
94     */
95    protected void updateOutline(NodeRealizer context) {
96      double x = context.getX();
97      double y = context.getY();
98      double width = context.getWidth();
99      double height = context.getHeight();
100     outline.setFrame(x, y, width, height);
101   }
102 
103   /**
104    * Calculates the annotation interior for the specified node.
105    * @param context The node context
106    */
107   protected void updateShape(NodeRealizer context) {
108     shape.reset();
109 
110     byte orientation;
111     GenericNodeRealizer gnr = (GenericNodeRealizer) context;
112     if (gnr.getStyleProperty(PROPERTY_ORIENTATION) != null) {
113       orientation = ((Byte) gnr.getStyleProperty(PROPERTY_ORIENTATION)).byteValue();
114     } else {
115       orientation = PROPERTY_ORIENTATION_VALUE_LEFT;
116     }
117     if (orientation == PROPERTY_ORIENTATION_VALUE_AUTO) {
118       orientation = determineBracketOrientation(context);
119     }
120     switch (orientation) {
121       case PROPERTY_ORIENTATION_VALUE_DOWN: {
122         createDownBracket(gnr.getX(), gnr.getY(), gnr.getWidth(), gnr.getHeight());
123         break;
124       }
125       case PROPERTY_ORIENTATION_VALUE_RIGHT: {
126         createRightBracket(gnr.getX(), gnr.getY(), gnr.getWidth(), gnr.getHeight());
127         break;
128       }
129       case PROPERTY_ORIENTATION_VALUE_TOP: {
130         createTopBracket(gnr.getX(), gnr.getY(), gnr.getWidth(), gnr.getHeight());
131         break;
132       }
133       case PROPERTY_ORIENTATION_VALUE_LEFT: {
134         createLeftBracket(gnr.getX(), gnr.getY(), gnr.getWidth(), gnr.getHeight());
135         break;
136       }
137       default: {
138         createLeftBracket(gnr.getX(), gnr.getY(), gnr.getWidth(), gnr.getHeight());
139       }
140     }
141   }
142 
143   private void createLeftBracket( double x, double y, double width, double height ) {
144     shape.moveTo((float)(x + 0.125 * width), (float) y);
145     shape.lineTo((float)x, (float)y);
146     shape.lineTo((float)x, (float)(y + height));
147     shape.lineTo((float)(x + 0.125 * width), (float)(y + height));
148   }
149 
150   private void createRightBracket( double x, double y, double width, double height ) {
151     shape.moveTo((float)(x + 0.875 * width),(float) y);
152     shape.lineTo((float)(x + width), (float)y);
153     shape.lineTo((float)(x + width), (float)(y + height));
154     shape.lineTo((float)(x + 0.875 * width), (float)(y + height));
155   }
156 
157   private void createTopBracket( double x, double y, double width, double height ) {
158     shape.moveTo((float)x, (float)(y + 0.125 * height));
159     shape.lineTo((float)x, (float)y);
160     shape.lineTo((float)(x + width), (float)y);
161     shape.lineTo((float)(x + width), (float)(y + 0.125 * height));
162   }
163 
164   private void createDownBracket( double x, double y, double width, double height ) {
165     shape.moveTo((float)x, (float)(y + 0.875 * height));
166     shape.lineTo((float)x, (float)(y + height));
167     shape.lineTo((float)(x + width), (float)(y + height));
168     shape.lineTo((float)(x + width), (float)(y + 0.875 * height));
169   }
170 
171   /**
172    * Returns a constant representing the orientation/placement of the
173    * annotation's bracket. One of
174    * <ul>
175    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_DOWN}</li>
176    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_RIGHT}</li>
177    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_TOP}</li>
178    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_LEFT}</li>
179    * </ul>
180    * @param context the context node
181    * @return one of {@link #PROPERTY_ORIENTATION_VALUE_DOWN},
182    * {@link #PROPERTY_ORIENTATION_VALUE_RIGHT},
183    * {@link #PROPERTY_ORIENTATION_VALUE_TOP}, and
184    * {@link #PROPERTY_ORIENTATION_VALUE_LEFT}.
185    */
186   private byte determineBracketOrientation(NodeRealizer context) {
187     final Node node = context.getNode();
188     if (node != null && node.degree() == 1) {
189       final Graph2D graph = (Graph2D) node.getGraph();
190       
191       final Point2D intersection =
192               node.inDegree() == 1
193               ? graph.getRealizer(node.firstInEdge()).getTargetIntersection()
194               : graph.getRealizer(node.firstOutEdge()).getSourceIntersection();
195 
196       final double x = intersection.getX();
197       final double y = intersection.getY();
198 
199       final double epsilon = 0.1;
200 
201       final double minX = context.getX();
202       if ((x + epsilon) > minX && (x - epsilon) < minX) {
203         return PROPERTY_ORIENTATION_VALUE_LEFT;
204       } else {
205         final double maxX = minX + context.getWidth();
206         if (((x + epsilon) > maxX && ((x - epsilon) < maxX))) {
207           return PROPERTY_ORIENTATION_VALUE_RIGHT;
208         } else {
209           final double minY = context.getY();
210           if ((y + epsilon) > minY && (y - epsilon) < minY) {
211             return PROPERTY_ORIENTATION_VALUE_TOP;
212           } else {
213             final double maxY = minY + context.getHeight();
214             if (((y + epsilon) > maxY && ((y - epsilon) < maxY))) {
215               return PROPERTY_ORIENTATION_VALUE_DOWN;
216             }
217           }
218         }
219       }
220     }
221 
222     return PROPERTY_ORIENTATION_VALUE_LEFT;
223   }
224 
225   public boolean contains(NodeRealizer context, double x, double y) {
226     updateOutline(context);
227     return outline != null && outline.contains(x, y);
228   }
229 }
230