1
14 package demo.layout.genealogy;
15
16 import java.awt.BasicStroke;
17 import java.awt.Color;
18 import java.awt.EventQueue;
19 import java.awt.Graphics2D;
20 import java.awt.Stroke;
21 import java.awt.event.ActionEvent;
22 import java.awt.geom.GeneralPath;
23 import java.awt.geom.PathIterator;
24 import java.io.File;
25 import java.io.FilenameFilter;
26 import java.io.IOException;
27 import java.net.URL;
28 import java.util.Locale;
29 import java.util.Map;
30
31 import javax.swing.AbstractAction;
32 import javax.swing.Action;
33 import javax.swing.JFileChooser;
34 import javax.swing.JMenu;
35 import javax.swing.JMenuBar;
36 import javax.swing.JToolBar;
37 import javax.swing.filechooser.FileFilter;
38
39 import demo.layout.genealogy.iohandler.GedcomHandler;
40 import demo.view.DemoBase;
41 import demo.view.DemoDefaults;
42
43 import y.base.Node;
44 import y.base.NodeCursor;
45 import y.base.NodeList;
46 import y.base.NodeMap;
47 import y.io.GraphMLIOHandler;
48 import y.io.graphml.KeyType;
49 import y.layout.genealogy.FamilyTreeLayouter;
50 import y.layout.LayoutTool;
51 import y.layout.BufferedLayouter;
52 import y.module.FamilyTreeLayoutModule;
53 import y.util.D;
54 import y.util.GraphHider;
55 import y.view.BendList;
56 import y.view.BevelNodePainter;
57 import y.view.BridgeCalculator;
58 import y.view.DefaultGraph2DRenderer;
59 import y.view.EdgeRealizer;
60 import y.view.GenericEdgePainter;
61 import y.view.GenericEdgeRealizer;
62 import y.view.GenericNodeRealizer;
63 import y.view.Graph2D;
64 import y.view.LineType;
65 import y.view.NavigationMode;
66 import y.view.NodeLabel;
67 import y.view.NodeRealizer;
68 import y.view.ShinyPlateNodePainter;
69 import y.geom.YPoint;
70 import y.view.SmartNodeLabelModel;
71
72
73
126 public class FamilyTreeDemo extends DemoBase {
127 private static final int PREFERRED_FONT_SIZE = 18;
128
129 private FamilyTreeLayoutModule ftlm;
130 private GraphHider graphHider;
131
132
135 public static void main(String[] args) {
136 EventQueue.invokeLater(new Runnable() {
137 public void run() {
138 Locale.setDefault(Locale.ENGLISH);
139 initLnF();
140 new FamilyTreeDemo().start();
141 }
142 });
143 }
144
145
148 protected void registerViewModes() {
149 view.addViewMode(new NavigationMode() {
150 public void mouseClicked(double x, double y) {
151 super.mouseClicked(x, y);
152 if(getHitInfo(x,y).getHitNode() != null) {
153 setToMain(getLastHitInfo().getHitNode());
154 }
155 }
156 });
157 }
158
159
166 private void setToMain(final Node clickedNode) {
167 final Graph2D graph = view.getGraph2D();
168 final YPoint oldCenter = graph.getCenter(clickedNode);
169
170 graphHider.unhideAll();
172 final NodeMap showNodeMap = graph.createNodeMap();
174 Node familyNode = (clickedNode.inDegree() > 0) ? clickedNode.firstInEdge().source() : null;
175 if(familyNode != null) {
176 for (NodeCursor nc = familyNode.successors(); nc.ok(); nc.next()) {
177 showNodeMap.setBool(nc.node(), true); }
179 NodeList queue = new NodeList(familyNode);
180 while (!queue.isEmpty()) { final Node n = queue.popNode();
182 if (!showNodeMap.getBool(n)) { showNodeMap.setBool(n, true);
184 queue.addAll(n.predecessors());
185 }
186 }
187 } else{
188 showNodeMap.setBool(clickedNode, true);
189 }
190
191 NodeList queue = new NodeList(clickedNode.successors());
193 while (!queue.isEmpty()) {
194 final Node n = queue.popNode();
195 if (!showNodeMap.getBool(n)) {
196 showNodeMap.setBool(n, true);
197 queue.addAll(n.successors());
198
199 for (NodeCursor nc = n.predecessors(); nc.ok(); nc.next()) {
201 final Node pred = nc.node();
202 if (!showNodeMap.getBool(pred)) {
203 showNodeMap.setBool(pred, true);
204 }
205 }
206 }
207 }
208
209 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
211 final Node n = nc.node();
212 if(!showNodeMap.getBool(n)) {
213 graphHider.hide(n);
214 }
215 }
216 graph.disposeNodeMap(showNodeMap);
217
218 final FamilyTreeLayouter layouter = new FamilyTreeLayouter();
220 getLayoutModule().configure(layouter);
221 new BufferedLayouter(layouter).doLayout(graph);
222
223 final YPoint newCenter = graph.getCenter(clickedNode);
225 LayoutTool.moveSubgraph(graph, graph.nodes(), oldCenter.getX() - newCenter.getX(),
226 oldCenter.getY() - newCenter.getY());
227 view.updateView();
228 }
229
230
233 protected JToolBar createToolBar() {
234 final Action layoutAction = new AbstractAction(
235 "Layout", SHARED_LAYOUT_ICON) {
236 public void actionPerformed(ActionEvent e) {
237 OptionSupport.showDialog(getLayoutModule(), view.getGraph2D(), true, view.getFrame());
238 }
239 };
240
241 final Action showAllAction = new AbstractAction("Show all") {
242 public void actionPerformed(ActionEvent e) {
243 if (graphHider != null) {
244 graphHider.unhideAll();
245 }
246 getLayoutModule().start(view.getGraph2D());
247 }
248 };
249
250 JToolBar jToolBar = super.createToolBar();
251 jToolBar.addSeparator();
252 jToolBar.add(showAllAction);
253 jToolBar.addSeparator();
255 jToolBar.add(createActionControl(layoutAction));
256 return jToolBar;
257 }
258
259 protected JMenuBar createMenuBar() {
260 final JMenuBar menuBar = super.createMenuBar();
261 createExamplesMenu(menuBar);
262 return menuBar;
263 }
264
265
268 protected void createExamplesMenu(JMenuBar menuBar) {
269 String fqResourceName = FamilyTreeDemo.class.getPackage().getName().replace('.', '/') + "/samples/kennedy_clan.ged";
270
271 URL resource = getClass().getResource("samples/kennedy_clan.ged");
272 if (resource == null) {
273 D.showError("Cannot load example files: missing resource " + fqResourceName + '\n' +
274 "Please ensure that your IDE recognizes \"*.ged\" files as resource files. \n" +
275 "Meanwhile you can load the sample files via the \"File/Load\" menu from the source folder of your distribution.");
276 return;
277 }
278
279 final String name = resource.getFile();
280 final String dirName = name.substring(0, name.lastIndexOf('/'));
281
282 final String[] dir = new File(dirName).list(new FilenameFilter() {
283 public boolean accept(File dir, String name) {
284 return name.toLowerCase().endsWith(".ged");
285 }
286 });
287
288 if (dir == null) {
289 D.showError("Cannot load example files: " + dirName + " not found");
290 return;
291 }
292
293 if (dir.length > 0) {
294 final JMenu menu = new JMenu("Example Graphs");
295 menuBar.add(menu);
296
297 for (int i = 0; i < dir.length; i++) {
298 final String fileName = dir[i];
299 menu.add(new AbstractAction(fileName) {
300 public void actionPerformed(ActionEvent e) {
301 loadGedcom(dirName + System.getProperty("file.separator") + fileName);
302 }
303 });
304 }
305
306 loadGedcom(dirName + System.getProperty("file.separator") + dir[0]);
307 }
308 }
309
310
314 private void loadGedcom(String name) {
315 final Graph2D graph = view.getGraph2D();
316 if (graphHider != null) {
317 graphHider.unhideAll();
318 }
319 graph.clear();
320 GedcomHandler gh = new GedcomHandler();
321 GraphMLIOHandler delegate = new GraphMLIOHandler();
322 NodeMap nodeTypeMap = graph.createNodeMap();
323 graph.addDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE, nodeTypeMap);
324 delegate.getGraphMLHandler().addInputDataAcceptor("GedcomType", nodeTypeMap, KeyType.STRING);
325
326 gh.setReaderDelegate(delegate);
327 try {
328 gh.read(graph, name);
329 } catch (IOException e1) {
330 D.show(e1);
331 }
332
333 for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
334 Node node = nodeCursor.node();
335 final NodeRealizer realizer = graph.getRealizer(node);
336 if (realizer.labelCount() > 0) {
337 final NodeLabel label = realizer.getLabel();
338 if (label.getFontSize() < PREFERRED_FONT_SIZE) {
339 label.setFontSize(PREFERRED_FONT_SIZE);
340
341 if (label.getWidth() + 2*label.getDistance() > realizer.getWidth()) {
342 realizer.setWidth(label.getWidth() + 2*label.getDistance() + 8);
343 }
344 }
345 SmartNodeLabelModel labelModel = new SmartNodeLabelModel();
346 label.setLabelModel(labelModel);
347 label.setModelParameter(labelModel.getDefaultParameter());
348 }
349 }
350
351 try {
352 getLayoutModule().start(view.getGraph2D());
353 } catch (Exception e1) {
354 D.show(e1);
355 }
356 }
357
358
359
364 protected Action createLoadAction() {
365 return new ImportAction();
366 }
367
368
369
372 public class ImportAction extends AbstractAction {
373 JFileChooser chooser;
374
375 public ImportAction() {
376 super("Load...");
377 chooser = null;
378 }
379
380 public void actionPerformed(ActionEvent e) {
381 if (chooser == null) {
382 chooser = new JFileChooser();
383 chooser.setFileFilter(new FileFilter() {
384 public boolean accept(File f) {
385 return f.isDirectory() || f.getName().toLowerCase().endsWith(".ged");
386 }
387
388 public String getDescription() {
389 return "Gedcom files";
390 }
391 });
392 }
393
394 if (chooser.showOpenDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
395 loadGedcom(chooser.getSelectedFile().toString());
396 }
397 }
398
399
400 }
401
402 private FamilyTreeLayoutModule getLayoutModule() {
403 if (ftlm == null) {
404 ftlm = new FamilyTreeLayoutModule();
405 ftlm.getOptionHandler().set("MALE_COLOR", DemoDefaults.DEFAULT_CONTRAST_COLOR);
406 ftlm.getOptionHandler().set("FEMALE_COLOR", DemoDefaults.DEFAULT_NODE_COLOR);
407 }
408 return ftlm;
409 }
410
411
415 protected class ExportAction extends AbstractAction {
416
417 private JFileChooser chooser;
418
419 public ExportAction() {
420 super("Export");
421 }
422
423
426 public void actionPerformed(ActionEvent e) {
427
428 if (chooser == null) {
429 chooser = new JFileChooser();
430 chooser.setFileFilter(new FileFilter() {
431 public boolean accept(File f) {
432 return f.isDirectory()
433 || f.getName().toLowerCase().endsWith(".ged");
434 }
435
436 public String getDescription() {
437 return "Gedcom files";
438 }
439 });
440 }
441 if (chooser.showOpenDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
442 String name = chooser.getSelectedFile().toString();
443 if (!name.toLowerCase().endsWith(".ged")) {
444 name = name + ".ged";
445 }
446
447 GedcomHandler gh = new GedcomHandler();
448 final Graph2D graph = view.getGraph2D();
449
450 try {
452 gh.write(graph, name);
453 } catch (IOException e1) {
454 D.show(e1);
455 }
456 }
457 }
458
459 }
460
461
463
466 protected void initialize() {
467
468 GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
470 Map implementationsMap = factory.createDefaultConfigurationMap();
471 ShinyPlateNodePainter spnp = new ShinyPlateNodePainter();
472 spnp.setDrawShadow(true);
473 implementationsMap.put(GenericNodeRealizer.Painter.class, spnp);
474 factory.addConfiguration("Individual", implementationsMap);
475
476 implementationsMap = factory.createDefaultConfigurationMap();
478 BevelNodePainter painter = new BevelNodePainter();
479 painter.setRadius(10);
480 painter.setDrawShadow(true);
481 implementationsMap.put(GenericNodeRealizer.Painter.class, painter);
482 factory.addConfiguration("Family", implementationsMap);
483
484 GenericEdgeRealizer.Factory edgeFactory = GenericEdgeRealizer.getFactory();
486 implementationsMap = edgeFactory.createDefaultConfigurationMap();
487 implementationsMap.put(GenericEdgeRealizer.Painter.class, new CustomEdgePainter());
488 edgeFactory.addConfiguration("Edge", implementationsMap);
489 GenericEdgeRealizer ger = new GenericEdgeRealizer();
490 ger.setConfiguration("Edge");
491 ger.setLineColor(new Color(0x808080));
492 ger.setLineType(LineType.LINE_2);
493 view.getGraph2D().setDefaultEdgeRealizer(ger);
494
495 BridgeCalculator bc = new BridgeCalculator();
497 bc.setCrossingStyle(BridgeCalculator.CROSSING_STYLE_GAP);
498 bc.setCrossingMode(BridgeCalculator.CROSSING_MODE_ORDER_INDUCED);
499 ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bc);
500
501 graphHider = new GraphHider(view.getGraph2D());
502
503
504 }
505
506
507
511 static final class CustomEdgePainter extends GenericEdgePainter {
512
513 protected GeneralPath adjustPath(EdgeRealizer context, BendList bends, GeneralPath path,
514 BridgeCalculator bridgeCalculator,
515 boolean selected) {
516 if (bridgeCalculator != null) {
517 GeneralPath p = new GeneralPath();
518 PathIterator pathIterator = bridgeCalculator.insertBridges(path.getPathIterator(null, 1.0d));
519 p.append(pathIterator, true);
520 return super.adjustPath(context, bends, p, bridgeCalculator, selected);
521 } else {
522 return super.adjustPath(context, bends, path, bridgeCalculator, selected);
523 }
524 }
525
526
527 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
528 Stroke s = gfx.getStroke();
529 Color oldColor = gfx.getColor();
530 if (s instanceof BasicStroke) {
531 Color c;
532 if (selected) {
533 initializeSelectionLine(context, gfx, selected);
534 c = gfx.getColor();
535 } else {
536 initializeLine(context, gfx, selected);
537 c = gfx.getColor();
538 gfx.setColor(new Color(128, 128, 128, 40));
539 gfx.translate(4, 4);
540 gfx.draw(path);
541 gfx.translate(-4, -4);
542 }
543 Color newC = selected ? Color.RED : c;
544 gfx.setColor(new Color(128 + newC.getRed() / 2, 128 + newC.getGreen() / 2, 128 + newC.getBlue() / 2));
545 gfx.translate(-1, -1);
546 gfx.draw(path);
547 gfx.setColor(new Color(newC.getRed() / 2, newC.getGreen() / 2, newC.getBlue() / 2));
548 gfx.translate(2, 2);
549 gfx.draw(path);
550 gfx.translate(-1, -1);
551 gfx.setColor(c);
552 gfx.draw(path);
553 gfx.setColor(oldColor);
554 } else {
555 gfx.draw(path);
556 }
557 }
558 }
559 }
560