1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
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-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.module;
15  
16  import y.base.DataProvider;
17  import y.base.Node;
18  import y.layout.ComponentLayouter;
19  import y.layout.LayoutOrientation;
20  import y.layout.genealogy.FamilyTreeLayouter;
21  import y.layout.hierarchic.IncrementalHierarchicLayouter;
22  import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
23  import y.layout.hierarchic.incremental.NodeLayoutDescriptor;
24  import y.module.LayoutModule;
25  import y.option.ConstraintManager;
26  import y.option.OptionGroup;
27  import y.option.OptionHandler;
28  import y.util.DataProviderAdapter;
29  import y.view.Graph2D;
30  import y.view.NodeRealizer;
31  import y.view.hierarchy.GroupLayoutConfigurator;
32  
33  import java.awt.Color;
34  
35  /**
36   * This module represents an interactive configurator and launcher for
37   * {@link y.layout.genealogy.FamilyTreeLayouter}.
38   * */
39  public class FamilyTreeLayoutModule extends LayoutModule {
40  
41    private static final String FAMILY_TREE_LAYOUTER = "FAMILY_TREE_LAYOUTER";
42  
43    // The colors used by the data provider to distiguish familiy nodes from individual nodes
44    private static final String FAMILY_PROPERTIES = "FAMILY_PROPERTIES";
45    private static final String FAMILY_COLOR = "FAMILY_COLOR";
46    private static final String MALE_COLOR = "MALE_COLOR";
47    private static final String FEMALE_COLOR = "FEMALE_COLOR";
48  
49    // Basic layout properties
50    private static final String LAYOUT = "LAYOUT";
51    private static final String ORIENTATION = "ORIENTATION";
52    private static final String RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
53    private static final String BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
54    private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
55    private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
56    private static final String SINGLE_DIRECT_BELOW = "SINGLE_DIRECT_BELOW";
57    private static final String FAMILIES_ALWAYS_BELOW = "FAMILIES_ALWAYS_BELOW";
58  
59    private static final String DISTANCES = "DISTANCES";
60    private static final String SIBLING_DISTANCES = "SIBLING_DISTANCES";
61    private static final String HORIZONTAL_SPACING = "HORIZONTAL_SPACING";
62    private static final String NODE_TO_NODE_DISTANCE = "NODE_TO_NODE_DISTANCE";
63  
64    private static final String GENERATION_DISTANCES = "GENERATION_DISTANCES";
65    private static final String VERTICAL_SPACING = "VERTICAL_SPACING";
66    private static final String MINIMUM_LAYER_DISTANCE = "MINIMUM_LAYER_DISTANCE";
67    private static final String MINIMUM_FIRST_SEGMENT = "MINIMUM_FIRST_SEGMENT";
68    private static final String MINIMUM_LAST_SEGMENT = "MINIMUM_LAST_SEGMENT";
69  
70    // Advanced layout properties
71    private static final String ADVANCED_LAYOUT = "ADVANCED_LAYOUT";
72    private static final String COMPONENT_LAYOUTER = "COMPONENT_LAYOUTER";
73    private static final String USE_COMPONENT_LAYOUTER = "USE_COMPONENT_LAYOUTER";
74    private static final String COMPONENT_DISTANCE = "COMPONENT_DISTANCE";
75    private static final String COMPONENT_STYLE = "COMPONENT_STYLE";
76    private static final String COMPONENT_STYLE_NONE = "COMPONENT_STYLE_NONE";
77    private static final String COMPONENT_STYLE_ROWS = "COMPONENT_STYLE_ROWS";
78    private static final String COMPONENT_STYLE_SINGLE_ROW = "COMPONENT_STYLE_SINGLE_ROW";
79    private static final String COMPONENT_STYLE_SINGLE_COLUMN = "COMPONENT_STYLE_SINGLE_COLUMN";
80    private static final String COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE = "COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE";
81    private static final String COMPONENT_STYLE_PACKED_RECTANGLE = "COMPONENT_STYLE_PACKED_RECTANGLE";
82    private static final String COMPONENT_STYLE_PACKED_CIRCLE = "COMPONENT_STYLE_PACKED_CIRCLE";
83    private static final String COMPONENT_STYLE_PACKED_COMPACT_CIRCLE = "COMPONENT_STYLE_PACKED_COMPACT_CIRCLE";
84    private static final String SORT_BY_SEX = "SORT_BY_SEX";
85    private static final String DO_NOT_SORT = "DO_NOT_SORT";
86    private static final String FEMALE_LEFT = "FEMALE_LEFT";
87    private static final String FEMALE_ALWAYS_LEFT = "FEMALE_ALWAYS_LEFT";
88    private static final String MALE_LEFT = "MALE_LEFT";
89    private static final String MALE_ALWAYS_LEFT = "MALE_ALWAYS_LEFT";
90  
91    private static final String NODE_ALIGNMENT = "NODE_ALIGNMENT";
92    private static final String NODE_ALIGN_TOP = "NODE_ALIGN_TOP";
93    private static final String NODE_ALIGN_CENTER = "NODE_ALIGN_CENTER";
94    private static final String NODE_ALIGN_BOTTOM = "NODE_ALIGN_BOTTOM";
95  
96    private static final Object[] sortBySexEnum = {DO_NOT_SORT, FEMALE_LEFT, FEMALE_ALWAYS_LEFT, MALE_LEFT,
97        MALE_ALWAYS_LEFT};
98    private static final Object[] orientEnum = {TOP_TO_BOTTOM, LEFT_TO_RIGHT, BOTTOM_TO_TOP, RIGHT_TO_LEFT};
99    private static final Object[] nodeAlignmentEnum = {NODE_ALIGN_TOP, NODE_ALIGN_CENTER, NODE_ALIGN_BOTTOM};
100   private static final Object[] componentStyleEnum =
101   {
102       COMPONENT_STYLE_NONE,
103       COMPONENT_STYLE_ROWS,
104       COMPONENT_STYLE_SINGLE_ROW,
105       COMPONENT_STYLE_SINGLE_COLUMN,
106       COMPONENT_STYLE_PACKED_RECTANGLE,
107       COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE,
108       COMPONENT_STYLE_PACKED_CIRCLE,
109       COMPONENT_STYLE_PACKED_COMPACT_CIRCLE,
110   };
111 
112   public FamilyTreeLayoutModule() {
113     super(FAMILY_TREE_LAYOUTER, "yFiles Layout Team", "Layouter for genealogic data");
114   }
115 
116   /**
117    * Creates an option handler for this class.
118    *
119    * @return The option handler for this module.
120    */
121   protected OptionHandler createOptionHandler() {
122     OptionHandler op = new OptionHandler(getModuleName());
123     ConstraintManager cm = new ConstraintManager(op);
124 
125     op.useSection(FAMILY_PROPERTIES);
126     op.addColor(FAMILY_COLOR, Color.black, true);
127     op.addColor(MALE_COLOR, new Color(0xCCCCFF), true);
128     op.addColor(FEMALE_COLOR, new Color(0xFF99CC), true);
129 
130     op.useSection(LAYOUT);
131     op.addEnum(ORIENTATION, orientEnum, 0);
132     op.addBool(SINGLE_DIRECT_BELOW, true);
133     op.addBool(FAMILIES_ALWAYS_BELOW, false);
134     op.addEnum(NODE_ALIGNMENT, nodeAlignmentEnum, 0);
135     op.addEnum(SORT_BY_SEX, sortBySexEnum, 0);
136 
137     op.useSection(DISTANCES);
138     OptionGroup ogSiblings = new OptionGroup();
139     ogSiblings.setAttribute(OptionGroup.ATTRIBUTE_TITLE, SIBLING_DISTANCES);
140     ogSiblings.addItem(op.addDouble(HORIZONTAL_SPACING, 40, 0, 400));
141     ogSiblings.addItem(op.addDouble(NODE_TO_NODE_DISTANCE, 40, 0, 400));
142     OptionGroup ogGenerations = new OptionGroup();
143     ogGenerations.setAttribute(OptionGroup.ATTRIBUTE_TITLE, GENERATION_DISTANCES);
144     ogGenerations.addItem(op.addDouble(VERTICAL_SPACING, 10, 0, 100));
145     ogGenerations.addItem(op.addDouble(MINIMUM_LAYER_DISTANCE, 40, 0, 400));
146     ogGenerations.addItem(op.addDouble(MINIMUM_FIRST_SEGMENT, 40, 0, 400));
147     ogGenerations.addItem(op.addDouble(MINIMUM_LAST_SEGMENT, 20, 0, 400));
148 
149     op.useSection(ADVANCED_LAYOUT);
150     op.addBool(USE_COMPONENT_LAYOUTER, true);
151     OptionGroup ogComp = new OptionGroup();
152     ogComp.setAttribute(OptionGroup.ATTRIBUTE_TITLE, COMPONENT_LAYOUTER);
153     ogComp.addItem(op.addInt(COMPONENT_DISTANCE, 40));
154     ogComp.addItem(op.addEnum(COMPONENT_STYLE, componentStyleEnum, 1));
155     cm.setEnabledOnValueEquals(USE_COMPONENT_LAYOUTER, Boolean.TRUE, COMPONENT_DISTANCE);
156 
157     return op;
158   }
159 
160   /** Main execution code to be implemented by any subclassed module. */
161   public void mainrun() {
162 
163     final OptionHandler op = getOptionHandler();
164 
165     final Graph2D graph = getGraph2D();
166 
167     /* Sets the properties for the inner layouter */
168     FamilyTreeLayouter layouter = new FamilyTreeLayouter();
169     layouter.setSpacingBetweenFamilyMembers(op.getDouble(HORIZONTAL_SPACING));
170     layouter.setOffsetForFamilyNodes(op.getDouble(VERTICAL_SPACING));
171     layouter.setFamilyNodesAlwaysBelow(op.getBool(FAMILIES_ALWAYS_BELOW));
172     layouter.setPartnerlessBelow(op.getBool(SINGLE_DIRECT_BELOW));
173 
174     /* Sets the orientation */
175 //    ((OrientationLayouter) layouter.getOrientationLayouter()).setMirrorMask(
176 //        OrientationLayouter.MIRROR_BOTTOM_TO_TOP | OrientationLayouter.MIRROR_LEFT_TO_RIGHT);
177     if (op.get(ORIENTATION).equals(TOP_TO_BOTTOM)) {
178       layouter.setLayoutOrientation(LayoutOrientation.TOP_TO_BOTTOM);
179     } else if (op.get(ORIENTATION).equals(LEFT_TO_RIGHT)) {
180       layouter.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
181     } else if (op.get(ORIENTATION).equals(BOTTOM_TO_TOP)) {
182       layouter.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
183     } else if (op.get(ORIENTATION).equals(RIGHT_TO_LEFT)) {
184       layouter.setLayoutOrientation(LayoutOrientation.RIGHT_TO_LEFT);
185     }
186 
187     /* Advanced */
188 
189     /* Component Layouter */
190     layouter.setComponentLayouterEnabled(op.getBool(USE_COMPONENT_LAYOUTER));
191     ComponentLayouter cl = (ComponentLayouter) layouter.getComponentLayouter();
192     cl.setComponentSpacing(op.getInt(COMPONENT_DISTANCE));
193     if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_NONE)) {
194       cl.setStyle(ComponentLayouter.STYLE_NONE);
195     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_CIRCLE)) {
196       cl.setStyle(ComponentLayouter.STYLE_PACKED_CIRCLE);
197     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_COMPACT_CIRCLE)) {
198       cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_CIRCLE);
199     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE)) {
200       cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE);
201     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_RECTANGLE)) {
202       cl.setStyle(ComponentLayouter.STYLE_PACKED_RECTANGLE);
203     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_ROWS)) {
204       cl.setStyle(ComponentLayouter.STYLE_ROWS);
205     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_SINGLE_COLUMN)) {
206       cl.setStyle(ComponentLayouter.STYLE_SINGLE_COLUMN);
207     } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_SINGLE_ROW)) {
208       cl.setStyle(ComponentLayouter.STYLE_SINGLE_ROW);
209     }
210 
211       IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
212       layouter.setTopLayouter(ihl);
213 
214       /* Vertical node alignment */
215       NodeLayoutDescriptor nld = ihl.getNodeLayoutDescriptor();
216       if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_TOP)) {
217         nld.setLayerAlignment(0.0);
218         layouter.setAlignment(FamilyTreeLayouter.ALIGN_TOP);
219       } else if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_CENTER)) {
220         nld.setLayerAlignment(0.5);
221         layouter.setAlignment(FamilyTreeLayouter.ALIGN_CENTER);
222       } else if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_BOTTOM)) {
223         nld.setLayerAlignment(1.0);
224         layouter.setAlignment(FamilyTreeLayouter.ALIGN_BOTTOM);
225       }
226 
227       ihl.setMinimumLayerDistance(op.getDouble(MINIMUM_LAYER_DISTANCE));
228       ihl.setNodeToNodeDistance(op.getDouble(NODE_TO_NODE_DISTANCE));
229       EdgeLayoutDescriptor eld = ihl.getEdgeLayoutDescriptor();
230       eld.setMinimumFirstSegmentLength(op.getDouble(MINIMUM_FIRST_SEGMENT));
231       eld.setMinimumLastSegmentLength(op.getDouble(MINIMUM_LAST_SEGMENT));
232       eld.setOrthogonallyRouted(true);
233 
234     if (op.get(SORT_BY_SEX).equals(DO_NOT_SORT)) {
235       layouter.setSortFamilyMembers(FamilyTreeLayouter.DO_NOT_SORT_BY_SEX);
236     } else  if (op.get(SORT_BY_SEX).equals(FEMALE_LEFT)) {
237       layouter.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_FIRST);
238     } else if (op.get(SORT_BY_SEX).equals(FEMALE_ALWAYS_LEFT)) {
239       layouter.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_ALWAYS_FIRST);
240     } else if (op.get(SORT_BY_SEX).equals(MALE_LEFT)) {
241       layouter.setSortFamilyMembers(FamilyTreeLayouter.MALE_FIRST);
242     } else if (op.get(SORT_BY_SEX).equals(MALE_ALWAYS_LEFT)) {
243       layouter.setSortFamilyMembers(FamilyTreeLayouter.MALE_ALWAYS_FIRST);
244     }
245 
246     /* Create the family data provider */
247     DataProvider dpType = null;
248     if(graph.getDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE) == null) {
249       /* Create the family info if not already existing */
250       dpType = new DataProviderAdapter() {
251         public Object get(Object o) {
252           NodeRealizer nr = graph.getRealizer((Node)o);
253           Color nodeColor = nr.getFillColor();
254           if (nodeColor != null && nodeColor.equals(op.get(MALE_COLOR))) {
255             return FamilyTreeLayouter.TYPE_MALE;
256           }
257           if (nodeColor != null && nodeColor.equals(op.get(FEMALE_COLOR))) {
258             return FamilyTreeLayouter.TYPE_FEMALE;
259           }
260           if (nodeColor != null && nodeColor.equals(op.get(FAMILY_COLOR))) {
261             return FamilyTreeLayouter.TYPE_FAMILY;
262           }
263           return null;
264         }
265       };
266       graph.addDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE, dpType);
267     }
268 
269 
270     GroupLayoutConfigurator glc = new GroupLayoutConfigurator(graph);
271 
272     try {
273       glc.prepareAll();
274       launchLayouter(layouter, true);
275     } finally {
276       glc.restoreAll();
277       if (dpType != null) {
278         graph.removeDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE);
279       }
280     }
281 
282   }
283 }
284