1
28 package demo.layout.genealogy;
29
30 import java.awt.BasicStroke;
31 import java.awt.Color;
32 import java.awt.EventQueue;
33 import java.awt.Graphics2D;
34 import java.awt.Stroke;
35 import java.awt.event.ActionEvent;
36 import java.awt.geom.GeneralPath;
37 import java.awt.geom.PathIterator;
38 import java.io.File;
39 import java.io.FilenameFilter;
40 import java.io.IOException;
41 import java.net.URL;
42 import java.util.Locale;
43 import java.util.Map;
44
45 import javax.swing.AbstractAction;
46 import javax.swing.Action;
47 import javax.swing.JFileChooser;
48 import javax.swing.JMenu;
49 import javax.swing.JMenuBar;
50 import javax.swing.JToolBar;
51 import javax.swing.filechooser.FileFilter;
52
53 import demo.layout.genealogy.iohandler.GedcomHandler;
54 import demo.layout.genealogy.iohandler.GedcomInputHandler;
55 import demo.layout.genealogy.iohandler.GedcomInputHandlerImpl;
56 import demo.view.DemoBase;
57
58 import y.base.Node;
59 import y.base.NodeCursor;
60 import y.base.NodeList;
61 import y.base.NodeMap;
62 import y.layout.BufferedLayouter;
63 import y.layout.genealogy.FamilyTreeLayouter;
64 import y.layout.LayoutTool;
65 import demo.layout.module.FamilyTreeLayoutModule;
66 import y.option.OptionHandler;
67 import y.util.D;
68 import y.util.GraphHider;
69 import y.view.Arrow;
70 import y.view.BendList;
71 import y.view.BridgeCalculator;
72 import y.view.DefaultGraph2DRenderer;
73 import y.view.EdgeRealizer;
74 import y.view.GenericEdgePainter;
75 import y.view.GenericNodeRealizer;
76 import y.view.Graph2D;
77 import y.view.NavigationMode;
78 import y.view.NodeLabel;
79 import y.view.NodeRealizer;
80 import y.view.ShapeNodePainter;
81 import y.view.ShinyPlateNodePainter;
82 import y.geom.YPoint;
83
84
85
136 public class FamilyTreeDemo extends DemoBase {
137 private static final int PREFERRED_FONT_SIZE = 18;
138 private static final Color COLOR_FAMILY = Color.BLACK;
139
140 private MyFamilyTreeLayoutModule ftlm;
141 private GraphHider graphHider;
142
143
146 public static void main(String[] args) {
147 EventQueue.invokeLater(new Runnable() {
148 public void run() {
149 Locale.setDefault(Locale.ENGLISH);
150 initLnF();
151 new FamilyTreeDemo().start();
152 }
153 });
154 }
155
156
159 protected void registerViewModes() {
160 view.addViewMode(new NavigationMode() {
161 public void mouseClicked(double x, double y) {
162 super.mouseClicked(x, y);
163 if(getHitInfo(x,y).getHitNode() != null) {
164 setToMain(getLastHitInfo().getHitNode());
165 }
166 }
167 });
168 }
169
170
177 private void setToMain(final Node clickedNode) {
178 final Graph2D graph = view.getGraph2D();
179 final YPoint oldCenter = graph.getCenter(clickedNode);
180
181 graphHider.unhideAll();
183 final NodeMap showNodeMap = graph.createNodeMap();
185 Node familyNode = (clickedNode.inDegree() > 0) ? clickedNode.firstInEdge().source() : null;
186 if(familyNode != null) {
187 for (NodeCursor nc = familyNode.successors(); nc.ok(); nc.next()) {
188 showNodeMap.setBool(nc.node(), true); }
190 NodeList queue = new NodeList(familyNode);
191 while (!queue.isEmpty()) { final Node n = queue.popNode();
193 if (!showNodeMap.getBool(n)) { showNodeMap.setBool(n, true);
195 queue.addAll(n.predecessors());
196 }
197 }
198 } else{
199 showNodeMap.setBool(clickedNode, true);
200 }
201
202 NodeList queue = new NodeList(clickedNode.successors());
204 while (!queue.isEmpty()) {
205 final Node n = queue.popNode();
206 if (!showNodeMap.getBool(n)) {
207 showNodeMap.setBool(n, true);
208 queue.addAll(n.successors());
209
210 for (NodeCursor nc = n.predecessors(); nc.ok(); nc.next()) {
212 final Node pred = nc.node();
213 if (!showNodeMap.getBool(pred)) {
214 showNodeMap.setBool(pred, true);
215 }
216 }
217 }
218 }
219
220 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
222 final Node n = nc.node();
223 if(!showNodeMap.getBool(n)) {
224 graphHider.hide(n);
225 }
226 }
227 graph.disposeNodeMap(showNodeMap);
228
229 final FamilyTreeLayouter layouter = new FamilyTreeLayouter();
231 getLayoutModule().configure(layouter);
232 new BufferedLayouter(layouter).doLayout(graph);
233
234 final YPoint newCenter = graph.getCenter(clickedNode);
236 LayoutTool.moveSubgraph(graph, graph.nodes(), oldCenter.getX() - newCenter.getX(),
237 oldCenter.getY() - newCenter.getY());
238 view.updateView();
239 }
240
241
244 protected JToolBar createToolBar() {
245 final Action layoutAction = new AbstractAction(
246 "Layout", SHARED_LAYOUT_ICON) {
247 public void actionPerformed(ActionEvent e) {
248 OptionSupport.showDialog(getLayoutModule(), view.getGraph2D(), true, view.getFrame());
249 }
250 };
251
252 final Action showAllAction = new AbstractAction("Show all") {
253 public void actionPerformed(ActionEvent e) {
254 if (graphHider != null) {
255 graphHider.unhideAll();
256 }
257 getLayoutModule().start(view.getGraph2D());
258 }
259 };
260
261 JToolBar jToolBar = super.createToolBar();
262 jToolBar.addSeparator();
263 jToolBar.add(showAllAction);
264 jToolBar.addSeparator();
266 jToolBar.add(createActionControl(layoutAction));
267 return jToolBar;
268 }
269
270
273 protected boolean isUndoRedoEnabled() {
274 return false;
275 }
276
277
280 protected boolean isClipboardEnabled() {
281 return false;
282 }
283
284 protected JMenuBar createMenuBar() {
285 final JMenuBar menuBar = super.createMenuBar();
286 createExamplesMenu(menuBar);
287 return menuBar;
288 }
289
290
293 protected void createExamplesMenu(JMenuBar menuBar) {
294 String fqResourceName = FamilyTreeDemo.class.getPackage().getName().replace('.', '/') + "/samples/kennedy_clan.ged";
295
296 URL resource = getResource("samples/kennedy_clan.ged");
297 if (resource == null) {
298 return;
299 }
300
301 final String name = resource.getFile();
302 final String dirName = name.substring(0, name.lastIndexOf('/'));
303
304 final String[] dir = new File(dirName).list(new FilenameFilter() {
305 public boolean accept(File dir, String name) {
306 return name.toLowerCase().endsWith(".ged");
307 }
308 });
309
310 if (dir == null) {
311 D.showError("Cannot load example files: " + dirName + " not found");
312 return;
313 }
314
315 if (dir.length > 0) {
316 final JMenu menu = new JMenu("Example Graphs");
317 menuBar.add(menu);
318
319 for (int i = 0; i < dir.length; i++) {
320 final String fileName = dir[i];
321 menu.add(new AbstractAction(fileName) {
322 public void actionPerformed(ActionEvent e) {
323 loadGedcom(dirName + System.getProperty("file.separator") + fileName);
324 }
325 });
326 }
327
328 loadGedcom(dirName + System.getProperty("file.separator") + dir[0]);
329 }
330 }
331
332
336 private void loadGedcom(String name) {
337 final Graph2D graph = view.getGraph2D();
338 if (graphHider != null) {
339 graphHider.unhideAll();
340 }
341 graph.clear();
342 final NodeMap types = graph.createNodeMap();
343 graph.addDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE, types);
344
345 final GedcomHandler gh = new GedcomHandler() {
346 public GedcomInputHandler createGedcomHandler(final Graph2D graph) {
347 return new GedcomInputHandlerImpl(graph) {
348 protected NodeRealizer createIndividualNodeRealizer(Graph2D graph) {
349 final GenericNodeRealizer realizer = new GenericNodeRealizer("Individual");
350 realizer.setSize(200, 80);
351 realizer.setFillColor(Color.WHITE);
352 realizer.setFillColor2(null);
353 return realizer;
354 }
355
356 protected NodeRealizer createFamilyNodeRealizer(Graph2D graph) {
357 final GenericNodeRealizer realizer = new GenericNodeRealizer("Family");
358 realizer.setSize(15, 15);
359 realizer.setFillColor(COLOR_FAMILY);
360 return realizer;
361 }
362
363 protected EdgeRealizer createWifeFamilyEdgeRealizer(Graph2D graph) {
364 return createEdgeRealizer(graph);
365 }
366
367 protected EdgeRealizer createHusbandFamilyEdgeRealizer(Graph2D graph) {
368 return createEdgeRealizer(graph);
369 }
370
371 protected EdgeRealizer createFamilyChildEdgeRealizer(Graph2D graph) {
372 return createEdgeRealizer(graph);
373 }
374
375 private EdgeRealizer createEdgeRealizer( final Graph2D graph ) {
376 final EdgeRealizer realizer = graph.getDefaultEdgeRealizer().createCopy();
377 realizer.setSourceArrow(Arrow.NONE);
378 realizer.setTargetArrow(Arrow.NONE);
379 return realizer;
380 }
381 };
382 }
383 };
384
385 try {
386 gh.read(graph, name);
387 } catch (IOException e1) {
388 D.show(e1);
389 }
390
391 for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
392 Node node = nodeCursor.node();
393 final NodeRealizer realizer = graph.getRealizer(node);
394 if (realizer.labelCount() > 0) {
395 final NodeLabel label = realizer.getLabel();
396 if (label.getFontSize() < PREFERRED_FONT_SIZE) {
397 label.setFontSize(PREFERRED_FONT_SIZE);
398
399 if (label.getWidth() + 2*label.getDistance() > realizer.getWidth()) {
400 realizer.setWidth(label.getWidth() + 2*label.getDistance() + 8);
401 }
402 }
403 }
404 }
405
406 try {
407 getLayoutModule().start(view.getGraph2D());
408 } catch (Exception e1) {
409 D.show(e1);
410 }
411 }
412
413
414
419 protected Action createLoadAction() {
420 return new ImportAction();
421 }
422
423 private MyFamilyTreeLayoutModule getLayoutModule() {
424 return ftlm == null ? ftlm = new MyFamilyTreeLayoutModule() : ftlm;
425 }
426
427 private class MyFamilyTreeLayoutModule extends FamilyTreeLayoutModule{
429 protected OptionHandler createOptionHandler() {
430 final OptionHandler options = super.createOptionHandler();
431 options.set(ITEM_MALE_COLOR, GedcomInputHandlerImpl.DEFAULT_COLOR_MALE);
432 options.set(ITEM_FEMALE_COLOR, GedcomInputHandlerImpl.DEFAULT_COLOR_FEMALE);
433 return options;
434 }
435
436 public void configure(FamilyTreeLayouter layouter) {
437 final OptionHandler options = getOptionHandler();
438 configure(layouter, options);
439 }
440 }
441
442
445 protected void initialize() {
446
447 GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
449 Map implementationsMap = factory.createDefaultConfigurationMap();
450 ShinyPlateNodePainter spnp = new ShinyPlateNodePainter();
451 spnp.setDrawShadow(true);
452 implementationsMap.put(GenericNodeRealizer.Painter.class, spnp);
453 factory.addConfiguration("Individual", implementationsMap);
454
455 implementationsMap = factory.createDefaultConfigurationMap();
457 ShapeNodePainter painter = new ShapeNodePainter(ShapeNodePainter.ELLIPSE);
458 implementationsMap.put(GenericNodeRealizer.Painter.class, painter);
459 factory.addConfiguration("Family", implementationsMap);
460
461 BridgeCalculator bc = new BridgeCalculator();
463 bc.setCrossingStyle(BridgeCalculator.CROSSING_STYLE_GAP);
464 bc.setCrossingMode(BridgeCalculator.CROSSING_MODE_ORDER_INDUCED);
465 ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bc);
466
467 graphHider = new GraphHider(view.getGraph2D());
468 }
469
470
474 static final class CustomEdgePainter extends GenericEdgePainter {
475
476 protected GeneralPath adjustPath(EdgeRealizer context, BendList bends, GeneralPath path,
477 BridgeCalculator bridgeCalculator,
478 boolean selected) {
479 if (bridgeCalculator != null) {
480 GeneralPath p = new GeneralPath();
481 PathIterator pathIterator = bridgeCalculator.insertBridges(path.getPathIterator(null, 1.0d));
482 p.append(pathIterator, true);
483 return super.adjustPath(context, bends, p, bridgeCalculator, selected);
484 } else {
485 return super.adjustPath(context, bends, path, bridgeCalculator, selected);
486 }
487 }
488
489
490 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
491 Stroke s = gfx.getStroke();
492 Color oldColor = gfx.getColor();
493 if (s instanceof BasicStroke) {
494 Color c;
495 if (selected) {
496 initializeSelectionLine(context, gfx, selected);
497 c = gfx.getColor();
498 } else {
499 initializeLine(context, gfx, selected);
500 c = gfx.getColor();
501 gfx.setColor(new Color(128, 128, 128, 40));
502 gfx.translate(4, 4);
503 gfx.draw(path);
504 gfx.translate(-4, -4);
505 }
506 Color newC = selected ? Color.RED : c;
507 gfx.setColor(new Color(128 + newC.getRed() / 2, 128 + newC.getGreen() / 2, 128 + newC.getBlue() / 2));
508 gfx.translate(-1, -1);
509 gfx.draw(path);
510 gfx.setColor(new Color(newC.getRed() / 2, newC.getGreen() / 2, newC.getBlue() / 2));
511 gfx.translate(2, 2);
512 gfx.draw(path);
513 gfx.translate(-1, -1);
514 gfx.setColor(c);
515 gfx.draw(path);
516 gfx.setColor(oldColor);
517 } else {
518 gfx.draw(path);
519 }
520 }
521 }
522
523
524
525
528 public class ImportAction extends AbstractAction {
529 JFileChooser chooser;
530
531 public ImportAction() {
532 super("Load...");
533 chooser = null;
534 }
535
536 public void actionPerformed(ActionEvent e) {
537 if (chooser == null) {
538 chooser = new JFileChooser();
539 chooser.setFileFilter(new FileFilter() {
540 public boolean accept(File f) {
541 return f.isDirectory() || f.getName().toLowerCase().endsWith(".ged");
542 }
543
544 public String getDescription() {
545 return "Gedcom files";
546 }
547 });
548 }
549
550 if (chooser.showOpenDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
551 loadGedcom(chooser.getSelectedFile().toString());
552 }
553 }
554 }
555
556
560 protected class ExportAction extends AbstractAction {
561 JFileChooser chooser;
562
563 public ExportAction() {
564 super("Export");
565 }
566
567 public void actionPerformed(ActionEvent e) {
568 if (chooser == null) {
569 chooser = new JFileChooser();
570 chooser.setFileFilter(new FileFilter() {
571 public boolean accept(File f) {
572 return f.isDirectory() || f.getName().toLowerCase().endsWith(".ged");
573 }
574
575 public String getDescription() {
576 return "Gedcom files";
577 }
578 });
579 }
580
581 if (chooser.showOpenDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
582 String name = chooser.getSelectedFile().toString();
583 if (!name.toLowerCase().endsWith(".ged")) {
584 name = name + ".ged";
585 }
586
587 GedcomHandler gh = new GedcomHandler();
588 final Graph2D graph = view.getGraph2D();
589
590 try {
592 gh.write(graph, name);
593 } catch (IOException e1) {
594 D.show(e1);
595 }
596 }
597 }
598 }
599 }
600