package edu.byu.cs.roots.opg.chart.selectvertical; import java.awt.Color; import java.awt.Font; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.util.ArrayList; import java.util.LinkedList; import javax.swing.JPanel; import edu.byu.cs.roots.opg.chart.ChartDrawInfo; import edu.byu.cs.roots.opg.chart.ChartMaker; import edu.byu.cs.roots.opg.chart.ChartOptions; import edu.byu.cs.roots.opg.chart.ShapeInfo; import edu.byu.cs.roots.opg.chart.cmds.DrawCmdMoveTo; import edu.byu.cs.roots.opg.chart.cmds.DrawCmdRelLineTo; import edu.byu.cs.roots.opg.chart.cmds.DrawCmdSetFont; import edu.byu.cs.roots.opg.chart.cmds.DrawCmdText; import edu.byu.cs.roots.opg.chart.preset.templates.StylingBoxScheme; import edu.byu.cs.roots.opg.fonts.OpgFont; import edu.byu.cs.roots.opg.gui.OnePageMainGui; import edu.byu.cs.roots.opg.model.Family; import edu.byu.cs.roots.opg.model.Gender; import edu.byu.cs.roots.opg.model.ImageFile; import edu.byu.cs.roots.opg.model.Individual; import edu.byu.cs.roots.opg.model.OpgOptions; import edu.byu.cs.roots.opg.model.OpgSession; import edu.byu.cs.roots.opg.model.PaperWidth; import edu.byu.cs.roots.opg.util.NameAbbreviator; public class SelectVerticalChartMaker implements ChartMaker { private static final long serialVersionUID = 1L; protected ChartDrawInfo chart = null; protected ChartMargins chartMargins = null; //options for current chart (this changes the state variables) protected VerticalChartOptions ops; //state variables protected float maxFont = 12, minFont=8; protected OpgFont font; protected double paperHeight; protected double paperWidth; protected int ancesGens = -1; protected int descGens = -1; protected boolean includeSpouses; protected boolean landscape = false; protected Individual root = null; //the root individual currently on the tree protected boolean roundedCorner = true; protected boolean boxBorder = true; protected boolean allowIntrude = true; protected boolean genLabels = true; //state changed flags (set by updateStateChanges()) protected boolean includeSpouseChanged = false; protected boolean paperSizeChanged = false; protected boolean fontChanged = false; protected boolean fontSizeChanged = false; protected boolean descGenChanged = false; protected boolean ancesGenChanged = false; protected boolean rootChanged = false; protected boolean landscapeChanged = false; protected boolean specificOpChanged = false; //Tree variables (dependent on the state variables) protected ArrayList<AncesTree> ancesTrees; protected ArrayList<DescTree> descTrees; protected double ancesMinHeight; protected double descMinHeight; protected AncesBox ancesBox = null; protected DescBox descBox = null; protected ArrayList<ArrayList<AncesBox>> ancesGenPositions; protected int[] maxSpouseLineOffset; protected int maxGensOnPage; protected double whiteSpace; //chart margin size - consider moving to VerticalChartOptions double marginSize = 72;//one-inch margins double headerSize = 0;//size (in points) of title(s) at top of chart double titleSize = 0;//size of chart title (to be implemented later) float labelFontSize = 12;//font size of generation labels at top of chart private boolean isPrimaryMaker = false; public ChartOptions convertToSpecificOptions(ChartOptions options) { VerticalChartOptions newOptions = new VerticalChartOptions(options); //set default values for options specific to Verical Chart here newOptions.setBoxBorder(true); newOptions.setRoundedCorners(true); newOptions.setDrawTitles(true); newOptions.setAllowIntrusion(true); newOptions.setMinPaperLength(12*72); // 1 foot return newOptions; } public void convertOpgOptions(OpgOptions options){ } public ChartDrawInfo getChart(ChartOptions options, OpgSession session) { // Account for margins and titles ops = (VerticalChartOptions) options; OpgOptions opgOptions = session.getOpgOptions(); //sets state change flags and updates state variables with the currently selected options updateStateChanges(session); //create new tree if root of spouse change / draw new chart if (chart == null || rootChanged || includeSpouseChanged || landscapeChanged) { chart = new ChartDrawInfo(0,0); chartMargins = new ChartMargins(chart, marginSize, headerSize); createTree(session); if(ancesGens <= opgOptions.getMaxAncesSlider()) { modifyChart(session); generateChart(session); } } //modify chart if settings change else if(paperSizeChanged || ancesGenChanged || descGenChanged || fontSizeChanged || fontChanged || specificOpChanged ) { BoxFormat.setFont(font); if(ancesGens <= opgOptions.getMaxAncesSlider()) { modifyChart(session); generateChart(session); } } return chart; } public JPanel getSpecificOptionsPanel(ChartOptions options, OnePageMainGui parent) { //options.setMinFontSize(12); //options.setMaxFontSize(200); return new VerticalChartOptionsPanel(options, parent); } protected void updateStateChanges(OpgSession session) { //set state change flags rootChanged = root != ops.getRoot(); includeSpouseChanged = includeSpouses != ops.isIncludeSpouses(); paperSizeChanged = ops.paperHeight() != paperHeight || ops.paperWidth() != paperWidth; ancesGenChanged = ancesGens != ops.getAncesGens(); descGenChanged = descGens != ops.getDescGens(); fontSizeChanged = maxFont != session.getOpgOptions().getMaxFontSize() || minFont != session.getOpgOptions().getMinFontSize(); fontChanged = font != session.getOpgOptions().getFont(); landscapeChanged = landscape != ops.isLandscape(); specificOpChanged = roundedCorner != ops.isRoundedCorners() || boxBorder != ops.isBoxBorder() || allowIntrude != ops.isAllowIntrusion() || genLabels != ops.isDrawTitles(); //update root = ops.getRoot(); ancesGens = ops.getAncesGens(); descGens = ops.getDescGens(); paperHeight = ops.paperHeight(); paperWidth = ops.paperWidth(); includeSpouses = ops.isIncludeSpouses(); maxFont = session.getOpgOptions().getMaxFontSize(); minFont = session.getOpgOptions().getMinFontSize(); font = session.getOpgOptions().getFont(); landscape = ops.isLandscape(); roundedCorner = ops.isRoundedCorners(); boxBorder = ops.isBoxBorder(); allowIntrude = ops.isAllowIntrusion(); genLabels = ops.isDrawTitles(); } /** * Build ancestor Trees (ancesTrees) and and descendant trees (descTress). Sets the maximum generation that * are able to fit on a page. */ protected void createTree(OpgSession session) { AncesBox.resetDuplicates(); OpgOptions opgOptions = session.getOpgOptions(); //set up list of AncesTree ancesTrees = new ArrayList<AncesTree>(); //add root ancesTree ancesTrees.add(new AncesTree(root)); //create main tree int maxOfMaxAncesGens = ancesTrees.get(0).ancesBox.maxGensInTree-1; //add spouse ancesTrees if (includeSpouses) for (Family fam : root.fams) { Individual spouse = (root.gender == Gender.MALE)? fam.wife : fam.husband; if (spouse != null) { ancesTrees.add(new AncesTree(spouse)); int spouseMaxAncesGens = ancesTrees.get(ancesTrees.size()-1).ancesBox.maxGensInTree-1; if (spouseMaxAncesGens > maxOfMaxAncesGens) maxOfMaxAncesGens = spouseMaxAncesGens; } } //initialize generation formats AncesBox.genFormats.clear(); for (int i=0; i < maxOfMaxAncesGens+1; i++) AncesBox.genFormats.add(BoxFormat.FORMATS.get(BoxFormat.FORMATS.size()-1)); //smallest box //Set numbers that label duplicates for (AncesTree tree: ancesTrees) tree.setDuplicateNumbers(); //System.out.println("maxancesgen = " + maxOfMaxAncesGens); //-----find max ances //maxGensOnPage = maxOfMaxAncesGens+1; whiteSpace = marginSize*2 + headerSize; double maxChartHeight = ancesMinHeight = (ops.isLandscape())? PaperWidth.maxChartWidth : PaperWidth.maxChartWidth*3; //get the height-constrained generation maxGensOnPage = ancesTrees.get(0).getGenAtMaxHieght(ancesMinHeight, BoxFormat.FORMATS.get(BoxFormat.FORMATS.size()-1)); //decrease number of ancestor generations until chart fits in maximum allowable length do { maxGensOnPage--; for (AncesTree tree: ancesTrees) tree.ancesBox.calcCoords(maxGensOnPage, 0); updateAncesHeight(); }while(ancesMinHeight + whiteSpace > maxChartHeight); //System.out.println("maxonpage = " + maxGensOnPage); //---end find max ances opgOptions.setMaxAncesSlider(maxGensOnPage, isPrimaryMaker); if(ops.getAncesGens() > opgOptions.getMaxAncesSlider()) { ops.setAncesGens(opgOptions.getMaxAncesSlider(), session); ancesGens = opgOptions.getMaxAncesSlider(); } //set up DescTrees for (Family fam: root.fams) fam.resetFlags(); descTrees = new ArrayList<DescTree>(); descTrees.add(new DescTree(root)); int maxOfMaxDescGens = descTrees.get(0).descBox.maxGensInTree-1; ops.setDescGens(0, session); if (ops.getDescGens() < maxOfMaxDescGens) { if (opgOptions.getMaxDescSlider() < maxOfMaxDescGens) opgOptions.setMaxDescSlider(maxOfMaxDescGens, isPrimaryMaker); // ops.setDescGens(maxOfMaxDescGens); } opgOptions.setMaxDescSlider(maxOfMaxDescGens, isPrimaryMaker); } protected void modifyChart(OpgSession session) { OpgOptions opgOptions = session.getOpgOptions(); //calculate needed size for generation labels setBoxWidth(); findLabelFontSize(); for (DescTree tree: descTrees) { tree.calcCoords(ops); tree.descBox.minHeight = tree.descBox.upperSubTreeHeight - tree.descBox.lowerSubTreeHeight; //expand boxes to fill gaps tree.descBox.vPos = 0; tree.descBox.setRelativePositions(0, ops.getDescGens()); updateDescHeight(); } updateWhiteSpace(); int maxGens = opgOptions.getMaxAncesSlider()+1; //reset generation formats for (int i=0; i < maxGens; i++) AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(7)); //smallest box //decrease number of ancestor generations until chart fits in maximum allowable length do { maxGens--; //ops.setAncesGens(maxGen); for (AncesTree tree: ancesTrees) tree.ancesBox.calcCoords(maxGens, 0); updateAncesHeight(); }while(ancesMinHeight + whiteSpace > ops.paperHeight()); maxGensOnPage = maxGens; //decrease number of descendant generations until chart fits in maximum allowable length while (descMinHeight + whiteSpace > ops.paperHeight()) { opgOptions.setMaxDescSlider(ops.getDescGens()-1, isPrimaryMaker); ops.setDescGens(opgOptions.getMaxDescSlider(), session); for (DescTree tree: descTrees) tree.calcCoords(ops); updateDescHeight(); updateWhiteSpace(); } //System.out.println("m:"+maxGensOnPage); //System.out.println("ag"+maxGensOnPage); if(ops.getAncesGens() > maxGensOnPage) { ops.setAncesGens(maxGensOnPage, session); ancesGens = maxGensOnPage; } increaseGenBoxSizes5(); } /**Alternate formatting algorithm */ protected void increaseGenBoxSizes1() { int maxGen = ancesGens; double whiteSpace = marginSize*2 + headerSize; //starting at the leaves increase generation box sizes until the chart doesn't fit on the page //for(int i=maxGen; i >= 0; i--) //go through all generation starting at leaves for(int i=0; i <= maxGen; i++) { int f = 0; //reset format box index do { if (f >= BoxFormat.FORMATS.size()) //no more formats? break; AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f++)); //set generation i's format ancesMinHeight = 0; //reset ancestor tree height //run spacing algorithm on all ances trees for (AncesTree tree: ancesTrees) { tree.ancesBox.calcCoords(maxGen, 0); ancesMinHeight += tree.ancesBox.upperSubTreeHeight - tree.ancesBox.lowerSubTreeHeight; } } while(ancesMinHeight + whiteSpace > ops.paperHeight()); //check if chart fits on the page } } /**Alternate formatting algorithm */ protected void increaseGenBoxSizes2() { int maxGen = ancesGens; double whiteSpace = marginSize*2 + headerSize; //starting at the leaves increase generation box sizes until the chart doesn't fit on the page for(int i=maxGen; i >= 0; i--) //go through all generation starting at leaves { int f = 0; //reset format box index do { if (f >= BoxFormat.FORMATS.size()) //no more formats? break; AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f++)); //set generation i's format ancesMinHeight = 0; //reset ancestor tree height //run spacing algorithm on all ances trees for (AncesTree tree: ancesTrees) { tree.ancesBox.calcCoords(maxGen, 0); ancesMinHeight += tree.ancesBox.upperSubTreeHeight - tree.ancesBox.lowerSubTreeHeight; } } while(ancesMinHeight + whiteSpace > ops.paperHeight()); //check if chart fits on the page } } /**Alternate formatting algorithm */ protected void increaseGenBoxSizes() { int maxGen = ancesGens; double whiteSpace = marginSize*2 + headerSize; int[] f = new int[ancesGens+1]; //reset generation formats for (int i=0; i <= maxGen; i++) { AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(0)); f[i] = 1; } int i=maxGen; while(true) { if (f[i] >= BoxFormat.FORMATS.size()) //no more formats? continue; AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f[i]++)); //set generation i's format ancesMinHeight = 0; //reset ancestor tree height //run spacing algorithm on all ances trees for (AncesTree tree: ancesTrees) { tree.ancesBox.calcCoords(maxGen, 0); ancesMinHeight += tree.ancesBox.upperSubTreeHeight - tree.ancesBox.lowerSubTreeHeight; } if (!(ancesMinHeight + whiteSpace > ops.paperHeight())) break; i = (i <= 0)? maxGen : i - 1; } } /**Alternate formatting algorithm */ protected void increaseGenBoxSizes4() { int maxGen = ancesGens; double whiteSpace = marginSize*2 + headerSize; int[] f = new int[ancesGens+1]; //reset generation formats for (int i=0; i <= maxGen; i++) { AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(0)); f[i] = 1; } int i=maxGen; while(true) { if (f[i] >= BoxFormat.FORMATS.size()) //no more formats? continue; if(i >= ancesTrees.get(0).getLargestGen()) { AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f[i]++)); //set generation i's format ancesMinHeight = 0; //reset ancestor tree height //run spacing algorithm on all ances trees for (AncesTree tree: ancesTrees) { tree.ancesBox.calcCoords(maxGen, 0); ancesMinHeight += tree.ancesBox.upperSubTreeHeight - tree.ancesBox.lowerSubTreeHeight; } } if (ancesMinHeight + whiteSpace <= ops.paperHeight()) break; i = (i <= 0)? maxGen : i - 1; } } /**Current formatting algorithm */ protected void increaseGenBoxSizes5() { int maxGen = ancesGens; int maxDiff = 1; int maxFormat = BoxFormat.FORMATS.size()-1; while(maxFormat >= 0 && BoxFormat.FORMATS.get(maxFormat).getMinHeight() > 1.1*BoxFormat.getGenWidth()) maxFormat--; //maxFormat = 7; updateWhiteSpace(); int[] f = new int[maxGen+1]; //reset generation formats for (int i=0; i <= maxGen; i++) { f[i] = BoxFormat.FORMATS.size()-1; AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f[i])); } int i=0; int stopGen = maxGen; while(true) { AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(f[i])); //set generation i's format //run spacing algorithm on all ances trees for (AncesTree tree: ancesTrees) tree.ancesBox.calcCoords(maxGen, 0); updateAncesHeight(); updateWhiteSpace(); if (ancesMinHeight + whiteSpace > ops.paperHeight()) { AncesBox.genFormats.set(i,BoxFormat.FORMATS.get(++f[i])); //decrease box size of current generation stopGen = i-1; } --f[i]; //increase box size if(f[i] > maxFormat) break; if (i < maxGen && (f[i+1] - f[i]) > maxDiff) //too much difference between generations break; if (f[i] < 0 || stopGen < 0) //no more formats or at the end break; i = (i >= stopGen)? 0 : i + 1; } //run spacing again for (AncesTree tree: ancesTrees) tree.ancesBox.calcCoords(maxGen, 0); updateAncesHeight(); } protected void updateAncesHeight() { //ancesMinHeight = ancesTrees.get(0).ancesBox.upperSubTreeHeight - ancesTrees.get(0).ancesBox.lowerSubTreeHeight; ancesMinHeight = 0; for (AncesTree tree: ancesTrees) { ancesMinHeight += tree.getHeight(); } } protected void updateDescHeight() { descMinHeight = 0; if(ops.isIncludeSpouses() && ops.getDescGens() > 0){ ArrayList<DescBox> descs = descTrees.get(0).descBox.children; for(DescBox box: descs) descMinHeight += box.upperSubTreeHeight - box.lowerSubTreeHeight; } else for (DescTree tree: descTrees) { descMinHeight += tree.descBox.upperSubTreeHeight - tree.descBox.lowerSubTreeHeight; } } protected void generateChart(OpgSession session) { //width and height vary based on paper orientation double paperHeight = ops.paperHeight(); double paperWidth = ops.paperWidth(); setBoxWidth(); //calculate needed size for generation labels findLabelFontSize(); //create new chart chart = new ChartDrawInfo((int)paperWidth, (int)paperHeight); chartMargins = new ChartMargins(chart, marginSize); paperHeight -= marginSize*2 + headerSize; paperWidth -= marginSize*2; //updateDescHeight(); //updateAncesHeight(); //double ancesHeight = ancesBox.upperSubTreeHeight - ancesBox.lowerSubTreeHeight; double ancesHeight = ancesMinHeight; double descHeight = descMinHeight;//descBox == null ? 0 : descBox.upperSubTreeHeight - descBox.lowerSubTreeHeight; //double chartHeight = Math.max(ancesHeight, descHeight); double ancesRootYPos = 0; double descRootYPos = 0; double rootXPos = AncesBox.boxWidth*11.0/10.0 * descGens; double scaler = 1; double dscaler = paperHeight / descHeight; //set height modifier so that the chart is the correct size if (ancesHeight >= descHeight && descTrees != null && ancesTrees != null) //ancestral tree is larger { scaler = paperHeight / ancesHeight; //double scaler = ancesBox.setHeight(paperHeight); for (AncesTree tree: ancesTrees) //tree.ancesBox.setScaler(scaler < 1.0 ? 1.0 : scaler); tree.ancesBox.setScaler(scaler < 1.0 ? 1.0 : scaler); for (DescTree tree: descTrees) if(ops.isIncludeSpouses() && ops.getDescGens() > 0) //tree.descBox.setScaler(dscaler < 1.0 ? 1.0 : dscaler); tree.descBox.setScaler(1); else //tree.descBox.setScaler(scaler < 1.0 ? 1.0 : scaler); tree.descBox.setScaler(1); //descBox.setScaler(scaler); ancesHeight *= scaler; //rootYPos = -(ancesBox.lowerSubTreeHeight*scaler); ancesRootYPos = 0; descRootYPos = -ancesTrees.get(ancesTrees.size()-1).ancesBox.lowerSubTreeHeight*scaler;//chart.getYExtent() / 2 - marginSize; descRootYPos += descTrees.get(0).descBox.lowerSubTreeHeight*scaler; if(ops.isIncludeSpouses() && ops.getDescGens() > 0) descRootYPos=0; } else if (descTrees != null && ancesTrees != null) //descendant tree is larger { scaler = paperHeight / descHeight;//descBox.setHeight(paperHeight); if(ops.isIncludeSpouses() && ops.getDescGens() > 0) scaler = paperHeight / ancesHeight; for (DescTree tree: descTrees) { if(ops.isIncludeSpouses() && ops.getDescGens() > 0) tree.descBox.setScaler(dscaler < 1.0 ? 1.0 : dscaler); else tree.descBox.setScaler(scaler < 1.0 ? 1.0 : scaler); } for (AncesTree tree: ancesTrees) { tree.ancesBox.setScaler(scaler < 1.0 ? 1.0 : scaler); } //ancesRootYPos = descRootYPos = -descTrees.get(0).descBox.lowerSubTreeHeight; descRootYPos = 0; ancesRootYPos = -descTrees.get(descTrees.size()-1).descBox.lowerSubTreeHeight*scaler;//chart.getYExtent() / 2 - marginSize; ancesRootYPos += ancesTrees.get(ancesTrees.size()-1).ancesBox.lowerSubTreeHeight*scaler; if(ops.isIncludeSpouses() && ops.getDescGens() > 0) ancesRootYPos=0; } //draw boxes on chart //ancesBox.drawAncesRootTree(chartMargins, ops, ancesGenPositions, 0, rootYPos); //descBox.drawDescRootTree(chartMargins, ops); double maxYPos=0,minYPos=paperHeight; if (ancesTrees != null) { for (int i = ancesTrees.size()-1; i >= 0; --i) { AncesTree tree = ancesTrees.get(i); ancesRootYPos += -(tree.ancesBox.lowerSubTreeHeight*scaler); tree.DrawTree(chartMargins, ops, rootXPos, ancesRootYPos); //if include spouse and descendants we need to connect some lines. if(ops.isIncludeSpouses() && ops.getDescGens() > 0){ if(ancesRootYPos > maxYPos) maxYPos = ancesRootYPos; if(ancesRootYPos < minYPos) minYPos = ancesRootYPos; double descXPos = AncesBox.boxWidth*11.0/10.0 * (descGens - 1); double midDistance = (rootXPos - descXPos - AncesBox.boxWidth)/2; chartMargins.addDrawCommand(new DrawCmdMoveTo(chartMargins.xOffset(rootXPos-midDistance),chartMargins.yOffset(ancesRootYPos))); chartMargins.addDrawCommand(new DrawCmdRelLineTo(midDistance,0,1,Color.black)); } ancesRootYPos += tree.ancesBox.upperSubTreeHeight* scaler; } } if (descTrees != null) { //works differently if spouse is included. if(ops.isIncludeSpouses() && ops.getDescGens() > 0){ descRootYPos=0; DescTree t = descTrees.get(descTrees.size()-1); ArrayList<DescBox> children = t.descBox.children; double descXPos = AncesBox.boxWidth*11.0/10.0 * (descGens - 1); double midDistance = (rootXPos - descXPos - AncesBox.boxWidth)/2; //Draw each child for (int i = children.size()-1; i >= 0; --i){ DescBox child = children.get(i); descRootYPos += -(child.lowerSubTreeHeight*dscaler); child.drawDescRootTree(session, chartMargins, ops, t.descGenPositions, descXPos, descRootYPos); chartMargins.addDrawCommand(new DrawCmdMoveTo(chartMargins.xOffset(descXPos+AncesBox.boxWidth),chartMargins.yOffset(descRootYPos))); chartMargins.addDrawCommand(new DrawCmdRelLineTo(midDistance,0,1,Color.black)); if(descRootYPos > maxYPos) maxYPos = descRootYPos; if(descRootYPos < minYPos) minYPos = descRootYPos; descRootYPos += child.upperSubTreeHeight*dscaler; } chartMargins.addDrawCommand(new DrawCmdMoveTo(chartMargins.xOffset(descXPos+AncesBox.boxWidth+midDistance),chartMargins.yOffset(minYPos))); chartMargins.addDrawCommand(new DrawCmdRelLineTo(0,maxYPos-minYPos,1,Color.black)); } else for (int i = descTrees.size()-1; i >= 0; --i) { DescTree tree = descTrees.get(i); descRootYPos += -(tree.descBox.lowerSubTreeHeight*scaler); tree.DrawTree(session, chartMargins, ops, rootXPos, descRootYPos); descRootYPos += tree.descBox.upperSubTreeHeight*scaler; } } //draw root connection lines if (descGens == 0) { //if only 2 spouses and no descendants, draw line between the two boxes if (ancesTrees.size() == 2) { AncesBox rootBox = ancesTrees.get(0).ancesBox; AncesBox spouseBox = ancesTrees.get(1).ancesBox; chartMargins.addDrawCommand(new DrawCmdMoveTo(chartMargins.xOffset( ops.isAllowIntrusion() ? AncesBox.boxWidth / 3.0: AncesBox.boxWidth / 2.0), chartMargins.yOffset( -spouseBox.lowerSubTreeHeight*scaler + spouseBox.upperRelBoxBound) )); chartMargins.addDrawCommand(new DrawCmdRelLineTo(0,((spouseBox.upperSubTreeHeight - rootBox.lowerSubTreeHeight)*scaler) - spouseBox.upperRelBoxBound + rootBox.lowerRelBoxBound,1,Color.BLACK )); } //: for 3 or more spouses, draw a connecting line to the side } //draw titles on chart if (ops.drawTitles) drawTitles(); //draw Logo on chart - branding drawLogo(); //ops.resetChanged(); } protected void drawTitles() { //choose font size for generation labels FontRenderContext frc = NameAbbreviator.frc; //draw each generation label Font font = OpgFont.getDefaultSansSerifFont(Font.BOLD, labelFontSize); LineMetrics lm = font.getLineMetrics("gjpqyjCAPSQJbdfhkl", frc); double horizPos = marginSize; double vertPos = ops.paperHeight(); vertPos += -marginSize - headerSize + (2*lm.getLeading()) + lm.getDescent(); //draw ancestor labels chart.addDrawCommand(new DrawCmdSetFont(font, Color.RED)); for(int gen = -ops.getDescGens(); gen <= ops.getAncesGens(); ++gen) { double width = font.getStringBounds(getGenerationLabel(gen), frc).getWidth(); //draw label centered above boxes for generation chart.addDrawCommand(new DrawCmdMoveTo(horizPos + (AncesBox.boxWidth - width)/2.0, vertPos )); chart.addDrawCommand(new DrawCmdText(getGenerationLabel(gen))); //draw spouse's name beneath root's name if root only has one spouse if (gen == 0 && ops.isIncludeSpouses() && root.fams.size() == 1) { double spouseVertPos = vertPos; spouseVertPos -= lm.getHeight(); width = font.getStringBounds("and", frc).getWidth(); chart.addDrawCommand(new DrawCmdMoveTo(horizPos + (AncesBox.boxWidth - width)/2.0, spouseVertPos )); chart.addDrawCommand(new DrawCmdText("and")); spouseVertPos -= lm.getHeight(); Individual spouse = (root.gender == Gender.MALE)? root.fams.get(0).wife : root.fams.get(0).husband; NameAbbreviator.nameFit(spouse.namePrefix.trim(), spouse.givenName.trim(), spouse.surname, spouse.nameSuffix, (float)AncesBox.boxWidth, font); String spouseName = NameAbbreviator.getName(); width = font.getStringBounds(spouseName, frc).getWidth(); chart.addDrawCommand(new DrawCmdMoveTo(horizPos + (AncesBox.boxWidth - width)/2.0, spouseVertPos )); chart.addDrawCommand(new DrawCmdText(spouseName)); } horizPos += AncesBox.boxWidth * 11.0/10.0; } } protected void drawLogo() { chartMargins.addDrawCommand(new DrawCmdMoveTo(marginSize+chartMargins.getXExtent()-150, marginSize-12)); chart.addDrawCommand(new DrawCmdSetFont(OpgFont.getDefaultSerifFont(Font.PLAIN, 12),Color.LIGHT_GRAY)); chart.addDrawCommand(new DrawCmdText("www.OnePageGenealogy.com")); } //this returns an appropriate label for the generation - gen - 0 = self, 1 = parents, -1 = children, etc. protected String getGenerationLabel(int gen) { switch (gen) { case 0: //return root.givenName + " " + root.middleName + " " + root.surname;// Font font = OpgFont.getDefaultSansSerifFont(Font.BOLD, labelFontSize); NameAbbreviator.nameFit(root.namePrefix.trim(), root.givenName.trim(), root.surname, root.nameSuffix, (float)AncesBox.boxWidth, font); return NameAbbreviator.getName(); case 1: return "Parents"; case 2: return "Grandparents"; case 3: return "Great-Grandparents"; case -1: return "Children"; case -2: return "Grandchildren"; case -3: return "Great-Grandchildren"; default: if (gen > 0) return gen-2 + getOrdinalSuffix(gen-2) + " Great-Grandparents"; else return (-gen)-2 + getOrdinalSuffix((-gen)-2) + " Great-Grandchildren"; } } protected static String getOrdinalSuffix(int gen) { if (11 <= (gen%100) && 13 >= (gen%100)) return "th"; switch (gen%10) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; } } protected void findLabelFontSize() { //find width of longest label FontRenderContext frc = NameAbbreviator.frc; float testFontSize = 72; Font font = OpgFont.getDefaultSansSerifFont(Font.BOLD, testFontSize); headerSize = 72; double longestWidth = 0; for(int gen = -ops.getDescGens(); gen <= ops.getAncesGens(); ++gen) { //if (gen == 0) continue; double width = font.getStringBounds(getGenerationLabel(gen), frc).getWidth(); if (width > longestWidth) longestWidth = width; } //set font size so that longest label barely fits over box setBoxWidth(); labelFontSize = (float)(testFontSize * (AncesBox.boxWidth / longestWidth)); final float MAXLABELFONTSIZE = 80; if(labelFontSize > MAXLABELFONTSIZE) labelFontSize=MAXLABELFONTSIZE; LineMetrics lm = font.deriveFont(labelFontSize).getLineMetrics("gjpqyjCAPSQJbdfhkl", frc); if (ops.isDrawTitles()) headerSize = titleSize + lm.getHeight() + lm.getLeading(); else headerSize = titleSize; } private void setBoxWidth() { BoxFormat.setUseGenWidth(true); double paperWidth = ops.paperWidth(); int numGens = ops.getAncesGens() + ops.getDescGens(); AncesBox.boxWidth = (paperWidth - 2*marginSize) / ((11.0/10.0 * numGens) + 1 ); DescBox.boxWidth = AncesBox.boxWidth; BoxFormat.setGenWidth(AncesBox.boxWidth); } public LinkedList<ShapeInfo> getChartShapes() { // Auto-generated method stub return null; } public void updateWhiteSpace() { if (ops.isDrawTitles()) { findLabelFontSize(); } whiteSpace = marginSize*2 + headerSize; } /** * Goes through all visible ancestors and descendants, returning a LinkedList of their ShapeInfo * @param max visible ancestors, chosen by user * @param max visible descendants, chosen by user * @return LinkedList of ShapeInfo of all visible people */ public LinkedList<ShapeInfo> getChartShapes(int maxAnc, int maxDesc, OpgSession session) { LinkedList<ShapeInfo> retVal = new LinkedList<ShapeInfo>(); for (AncesTree tree: ancesTrees) retVal = tree.ancesBox.getBoxes(retVal, 0, maxAnc, session); for (DescTree tree: descTrees) retVal = tree.descBox.getBoxes(retVal, 0, maxDesc); return retVal; } /** * Goes through all visible ancestors and descendants, checking if the passed in point intersects with them. * If so, returns the ShapeInfo of that person. * @param x coord of click * @param y coord of click * @param max visible ancestors chosen by user * @param max visible descendants chosen by user * @return the ShapeInfo of the clicked person. Null if no intersect. */ public ShapeInfo getIndiIntersect(double x, double y, int maxAnc, int maxDesc, OpgSession session) { for (AncesTree tree: ancesTrees) { ShapeInfo retVal = tree.ancesBox.checkIntersect(x, y, 0, maxAnc, session); if (retVal != null) return retVal; } for (DescTree tree: descTrees) { ShapeInfo retVal = tree.descBox.checkIntersect(x, y, 0, maxDesc); if (retVal != null) { return retVal; } // if (retVal == null) // { // System.out.println("NULL BOX RETURNED? HUH?!"); // } } return null; } @Override public void setChartStyle(StylingBoxScheme style) { } @Override public StylingBoxScheme getBoxStyles() { return null; } @Override public ArrayList<ImageFile> getImages() { return null; } @Override public void setIsPrimaryMaker(boolean set) { isPrimaryMaker = set; } }