package edu.byu.cs.roots.opg.chart.working; 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 org.apache.log4j.Logger; import edu.byu.cs.roots.opg.chart.ChartDrawInfo; import edu.byu.cs.roots.opg.chart.ChartMaker; import edu.byu.cs.roots.opg.chart.ChartMarginData; 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.DrawCmdPicture; 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.ChartMargins; import edu.byu.cs.roots.opg.chart.preset.templates.StylingBoxScheme; import edu.byu.cs.roots.opg.color.ColorScheme; import edu.byu.cs.roots.opg.fonts.OpgFont; import edu.byu.cs.roots.opg.gui.OnePageMainGui; 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; import edu.byu.cs.roots.opg.util.PlaceAbbreviator; public class WorkingVerticalFixedChartMaker implements ChartMaker { private static final long serialVersionUID = 1L; public static Logger log = Logger.getLogger(WorkingVerticalFixedChartMaker.class); protected ChartDrawInfo chart = null; protected ChartMargins chartMargins = null; protected WorkingVerticalFixedChartOptions ops; protected int ancesGens = -1; protected int descGens = -1; protected Individual root; //the root individual currently on the tree //chart margin size - consider moving to VerticalChartOptions //TODO switch to changeable margins ChartMarginData marginStorage = new ChartMarginData(); 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 = 11;//font size of generation labels at top of chart //ToDo: make sure that the labelFontSize can be made bigger or smaller! double boxWidth=0.0; int maxGensThatFit=0; protected float oneLine; protected ArrayList<ImageFile> images = new ArrayList<ImageFile>(); private boolean isPrimaryMaker = false; public ChartOptions convertToSpecificOptions(ChartOptions options) { WorkingVerticalFixedChartOptions newOptions = new WorkingVerticalFixedChartOptions(options); newOptions.setDrawTitles(true); newOptions.setPaperHeightChoice(true); return newOptions; } public void convertOpgOptions(OpgOptions options){ options.setPreferredLength(false, 0); } public ChartDrawInfo getChart(ChartOptions options, OpgSession session) { ops = (WorkingVerticalFixedChartOptions) options; if (session.isChanged()) { initializeChart(session); root = ops.getRoot(); ancesGens = ops.getAncesGens(); //chart.updateSortedCharts(); } return chart; } public JPanel getSpecificOptionsPanel(ChartOptions options, OnePageMainGui parent) { return new WorkingVerticalFixedOptionsPanel(options, parent); } protected void initializeChart(OpgSession session) { double chartWidth = (ops.isLandscape())? ops.getPaperLength() : ops.getPaperWidth().width; double chartHeight = (ops.isLandscape())? ops.getPaperWidth().width : ops.getPaperLength(); double usableChartHeight = chartHeight - marginSize * 2.0; OpgOptions opgOptions = session.getOpgOptions(); // Find the size for the Name // FontRenderContext frc = new FontRenderContext(new AffineTransform(), false, true); Font testFont = opgOptions.getFont().font.deriveFont(opgOptions.getMinFontSize()); LineMetrics lm = testFont.getLineMetrics("gjpqyjCAPSQJbdfhkl", NameAbbreviator.frc); // Calculate the Max Generations that we can fit on a chart and set the UI // appropriately. double maxChartHeight = (ops.isLandscape()) ? PaperWidth.maxChartWidth : PaperWidth.maxChartWidth*3; double maxUsableChartHeight = maxChartHeight - marginSize * 2.0; oneLine = lm.getHeight(); //Math.round(ops.getMinFontSize() * 1.6); ops.setMinPaperLength(oneLine + (marginSize*2.0)); int maxGensAvaliable = (int)Math.floor(Math.log10(maxUsableChartHeight / oneLine )/Math.log10(2)); //-1 ; // + 1;// - 1; maxGensThatFit = (int)Math.floor(Math.log10(usableChartHeight / oneLine )/Math.log10(2));//-1 ; // + 1;// - 1; opgOptions.setMaxAncesSlider(maxGensAvaliable, isPrimaryMaker); opgOptions.setMaxDescSlider(0, isPrimaryMaker); if (ops.getAncesGens() > maxGensAvaliable) ops.setAncesGens(maxGensAvaliable, session); /** COLOR SCHEMES **/ ColorScheme ancesScheme = ops.getAncesScheme(); ColorScheme descScheme = ops.getDescScheme(); descScheme.clearTree(); descScheme.colorTree(ops.getRoot(), ColorScheme.colordown); ancesScheme.clearTree(); ancesScheme.colorTree(ops.getRoot(), ColorScheme.colorup); // Page resizing if (maxGensThatFit < Math.min(ops.getAncesGens(),maxGensAvaliable)) { chartHeight = (Math.pow(2,ops.getAncesGens()) * oneLine) + (marginSize * 2.0); // If we are in landscape mode then we have to format the slider bar if (ops.isLandscape()) { if (chartHeight > ops.getPaperWidth().width) ops.setPaperWidth(PaperWidth.findClosestFit(chartHeight)); chartHeight = ops.getPaperWidth().width; // if (chartHeight > PaperWidth.wd3.width) // { // chartHeight = PaperWidth.wd3.width; // ops.setPaperWidth(PaperWidth.wd3); // } // else if (chartHeight > PaperWidth.wd2.width) // { // chartHeight = PaperWidth.wd3.width; // ops.setPaperWidth(PaperWidth.wd3); // } // else if (chartHeight > PaperWidth.wd1.width) // { // chartHeight = PaperWidth.wd2.width; // ops.setPaperWidth(PaperWidth.wd2); // } // else // { // chartHeight = PaperWidth.wd1.width; // ops.setPaperWidth(PaperWidth.wd1); // } } else { ops.setPaperLength(chartHeight); ops.setMinPaperLength(chartHeight); } // Recalculate the Maximum number of generations to fit on this chart usableChartHeight = chartHeight - marginSize * 2.0; maxGensThatFit = (int)Math.floor(Math.log10(usableChartHeight / oneLine )/Math.log10(2)); //-1 ; // + 1;// - 1; } chart = new ChartDrawInfo((int)chartWidth, (int)chartHeight); maxGensThatFit = Math.min(maxGensThatFit, ops.getAncesGens()); //make sure that oneLine fits on the last one! double SpacePerPerson = usableChartHeight/Math.pow(2, ops.getAncesGens()+1); if ((SpacePerPerson + 5) < oneLine) { do { testFont = opgOptions.getFont().font.deriveFont(opgOptions.getMinFontSize()); lm = testFont.getLineMetrics("gjpqyjCAPSQJbdfhkl", NameAbbreviator.frc); oneLine = lm.getHeight(); } while (oneLine > SpacePerPerson + 5); } // See how wide the information of a person can be. boxWidth = (chartWidth-marginSize*2.0) / ((11.0/10.0 * maxGensThatFit) + 1);// //make an array that controls the size of a generation's font float fontGenSizes[] = new float[maxGensThatFit+1]; float div = 0; if (maxGensThatFit != 0) { div = (opgOptions.getMaxFontSize() - opgOptions.getMinFontSize())/maxGensThatFit; } for (int i = 0; i < (maxGensThatFit+1); i++) { fontGenSizes[i] = opgOptions.getMaxFontSize() - (float)(i * div); } //draw the chart! // chart.addDrawCommand(new DrawCmdSetFont(ops.getFont().font.deriveFont(oneLine), Color.BLACK)); drawPersonLine(session, marginSize, chartHeight/2.0, chartHeight-2*marginSize, 0, ops.getRoot(), null, fontGenSizes); root = ops.getRoot(); if (ops.drawTitles) drawTitles(); drawLogo(chartWidth, chartHeight); for (ImageFile f : images){ chart.addDrawCommand(new DrawCmdMoveTo(f.x, f.y)); chart.addDrawCommand(new DrawCmdPicture(f.width, f.height, f.getImage())); } session.resetChanged(); } /** * drawPersonLine is an unstoppable force. Resistance is futile. All your base are belong to us. * * * @param baseX * @param baseY * @param heightLeft * @param currentGen * @param indi */ protected void drawPersonLine(OpgSession session, double baseX, double baseY, double heightLeft, int currentGen, Individual indi, Individual spouseIndi, float[] fontGenSizes) { /*Notes of things to add: * CHECK! 1) names and things so they all fit nicely like * CHECK! 2) Fix font size so that it scales according to some height thingy..??? * DONE! 3) Fix width if (width of text > length of box) shrink text; * CHECK! 4) Ordinance work & colors! * CHECK! 5) Fix sliders/text size inputs * CHECK! 6) Fix marriage dates! * CHECK! 7) Titles & Logo * 8) Couples!!!! * 9) Descendants * 10) Add Options to the Advance Tab (Apply Color Scheme, Show LDS Information) */ OpgOptions opgOptions = session.getOpgOptions(); //If we maxed out the generations then stop if(currentGen <= maxGensThatFit) { //DRAW THE LINE chart.addDrawCommand(new DrawCmdMoveTo(baseX, baseY)); chart.addDrawCommand(new DrawCmdRelLineTo(boxWidth, 0, 1, Color.black)); //ADD CONNECTING LINES if (currentGen < maxGensThatFit) { chart.addDrawCommand(new DrawCmdRelLineTo(boxWidth/10.0, 0, 1.0, Color.black)); chart.addDrawCommand(new DrawCmdRelLineTo(0.0, heightLeft / 4.0, 1.0, Color.black)); chart.addDrawCommand(new DrawCmdRelLineTo(0.0, -heightLeft / 2.0, 1.0, Color.black)); } //add in LDS ordinance data String ordinancesComplete = ""; if(indi!=null) { String beps = ""; if(indi.baptism) beps += (indi.baptismComplete) ? "B" : "b"; if(indi.endowment) beps += (indi.endowmentComplete) ? "E" : "e"; if(indi.sealingToParents) beps += (indi.sealingToParentsComplete) ? "P" : "p"; if(indi.sealingToSpouse) beps += (indi.sealingToSpouseComplete) ? "S" : "s"; if (!beps.equals("")) ordinancesComplete += " - " + beps; } // Determine the Font Color by ordinance work Color infoColor = (indi == null) ? Color.BLACK : (ops.getAncesScheme().getColor(indi.id)); infoColor = (infoColor == Color.WHITE) ? Color.BLACK : infoColor; int maxColor = Math.max(infoColor.getBlue(), Math.max(infoColor.getRed(), infoColor.getGreen())); int maxValue = 125; if (maxColor > maxValue) { double divider = maxColor / maxValue; infoColor = new Color((int)(infoColor.getRed() / divider), (int)(infoColor.getGreen()/ divider), (int)(infoColor.getBlue()/ divider)); } //ADD TEXT String abbrName = "Name : "; if (indi != null) { NameAbbreviator.nameFit(indi.namePrefix.trim(), indi.givenName.trim(), indi.surname.trim(), indi.nameSuffix.trim(), (float)boxWidth, opgOptions.getFont().font.deriveFont(fontGenSizes[currentGen])); abbrName = NameAbbreviator.getName(); } //draw name correctly using the abbreviated name for the user selected font size Font testFont = opgOptions.getFont().font.deriveFont(fontGenSizes[currentGen]); LineMetrics lm = testFont.getLineMetrics(abbrName, NameAbbreviator.frc); double XPos = baseX+lm.getBaselineIndex()+5; double YPos = baseY+lm.getLeading()+ lm.getDescent(); chart.addDrawCommand(new DrawCmdSetFont(opgOptions.getFont().font.deriveFont(fontGenSizes[currentGen]), infoColor)); chart.addDrawCommand(new DrawCmdMoveTo(XPos, YPos)); chart.addDrawCommand(new DrawCmdText(abbrName + ordinancesComplete)); //because of lack of room, the information can only be so big. the constant //number that i am checking against is one third of the boxWidth. if the font //gets bigger than that, then the information will not be as big as the name double biggestInfo = boxWidth/3; Font font; double width; float size = fontGenSizes[currentGen]+1; do { size--; font = opgOptions.getFont().font.deriveFont(size); width = font.getStringBounds("Birth PL: ", NameAbbreviator.frc).getWidth(); } while (biggestInfo < width); lm = font.getLineMetrics("JQupgyBIWo/FhlY()PAS", NameAbbreviator.frc); //Determine the size given to each individual in the tree //width is always the same, so you don't have to worry about that double chartHeight = (ops.isLandscape())? ops.getPaperWidth().width: ops.getPaperLength(); //account for the margins chartHeight = chartHeight - 2*marginSize; int GenAmt = (int) Math.pow(2, (double) currentGen); //included the already added names to the chartHeight LineMetrics lmName = testFont.getLineMetrics("JQupgyBIWo/FhlYPAS", NameAbbreviator.frc); chartHeight = chartHeight - GenAmt * lmName.getHeight(); double spacePerPerson = chartHeight/GenAmt; int amtText =(int)((int) spacePerPerson/lm.getHeight()); float linesPossible = 6; if (amtText < 6) linesPossible = amtText; if (abbrName != null && abbrName.equals("David John Bever")) { System.out.println("This is the dude!"); if(indi.fams.get(0).divorce) System.out.println("DIVORCED!!!"); } //this will calculate how much width is taken by the beginning letters(e.g. 'D: ' or 'Birth: ') width = 0; //create additional information which uses linesPossible String AdditionalInfo[] = null; try { AdditionalInfo = new String[6]; String[] marriageInfo = getMarriageDateAndPlace(indi, spouseIndi); if (linesPossible <= 5) { AdditionalInfo[0] = "B: " + ((indi == null || indi.birth == null || indi.birth.date == null) ? "" : indi.birth.date); //check to see if the place is included, otherwise don't do the operations! if (indi != null && indi.birth != null && indi.birth.date != null && !indi.birth.place.equals("")) { AdditionalInfo[0] += "/"; width = font.getStringBounds(AdditionalInfo[0], NameAbbreviator.frc).getWidth(); AdditionalInfo[0] += PlaceAbbr(session, indi.birth.place, width, size); } if (linesPossible == 2 || linesPossible > 2 && indi != null && indi.gender == Gender.FEMALE) { AdditionalInfo[1] = "D: " + ((indi == null || indi.death == null || indi.death.date == null) ? "" : indi.death.date); if (indi != null && indi.death != null && indi.death.place != null && !indi.birth.place.equals("")) { AdditionalInfo[1] += "/"; width = font.getStringBounds(AdditionalInfo[1], NameAbbreviator.frc).getWidth(); AdditionalInfo[1] += PlaceAbbr(session, indi.death.place, width, size); } linesPossible = 2; } else if (linesPossible > 2) { AdditionalInfo[1] = "M: " + ((marriageInfo[0] == null) ? "" : marriageInfo[0]); if (marriageInfo[1] != null && !marriageInfo[1].equals("")) { AdditionalInfo[1] += "/"; width = font.getStringBounds(AdditionalInfo[1], NameAbbreviator.frc).getWidth(); AdditionalInfo[1] += PlaceAbbr(session, marriageInfo[1], width, size); } AdditionalInfo[2] = "D: " + ((indi == null || indi.death == null || indi.death.date == null) ? "" : indi.death.date); if (indi != null && indi.death != null && indi.death.place != null && !indi.birth.place.equals("")) { AdditionalInfo[2] += "/"; width = font.getStringBounds(AdditionalInfo[2], NameAbbreviator.frc).getWidth(); AdditionalInfo[2] += PlaceAbbr(session, indi.death.place, width, size); } linesPossible = 3; } }else { AdditionalInfo[0] = "Birth: " + ((indi == null || indi.birth == null || indi.birth.date == null) ? "" : indi.birth.date); AdditionalInfo[1] = " "; width = font.getStringBounds(AdditionalInfo[1], NameAbbreviator.frc).getWidth(); AdditionalInfo[1] += ((indi == null || indi.birth == null || indi.birth.place == null) ? "" : PlaceAbbr(session, indi.birth.place, width, size)); if(indi!=null && linesPossible!=0 && indi.gender == Gender.FEMALE ) { AdditionalInfo[2] = "Death: " + ((indi == null || indi.death == null || indi.death.date == null) ? "" : indi.death.date); AdditionalInfo[3] = " "; width = font.getStringBounds(AdditionalInfo[3], NameAbbreviator.frc).getWidth(); AdditionalInfo[3] += ((indi == null || indi.death == null || indi.death.place == null) ? "" : PlaceAbbr(session, indi.death.place, width, size)); linesPossible = 4; } else { AdditionalInfo[2] = "Marriage: " + ((marriageInfo[2] == null) ? "" : marriageInfo[2]) + ((marriageInfo[0] == null) ? "" : marriageInfo[0]); AdditionalInfo[3] = " "; width = font.getStringBounds(AdditionalInfo[3], NameAbbreviator.frc).getWidth(); AdditionalInfo[3] += ((marriageInfo[1] == null) ? "" : PlaceAbbr(session, marriageInfo[1], width, size)); } AdditionalInfo[4] = "Death: " + ((indi == null || indi.death == null || indi.death.date == null) ? "" : indi.death.date); AdditionalInfo[5] = " "; width = font.getStringBounds(AdditionalInfo[5], NameAbbreviator.frc).getWidth(); AdditionalInfo[5] += ((indi == null || indi.death == null || indi.death.place == null) ? "" : PlaceAbbr(session, indi.death.place, width, size)); } } catch(NullPointerException e) { e.getMessage(); } //Add the Additional Personal Info if (AdditionalInfo != null) { //add all the lines to the chart for (int LineNum = 0; LineNum < (int)linesPossible; LineNum++) { chart.addDrawCommand(new DrawCmdSetFont(opgOptions.getFont().font.deriveFont(size), infoColor)); chart.addDrawCommand(new DrawCmdMoveTo(baseX+10, baseY-(1+lm.getAscent()+(size * LineNum)))); if (AdditionalInfo[LineNum] != null) chart.addDrawCommand(new DrawCmdText(AdditionalInfo[LineNum])); } } //recurse to parent-descendants Individual Father = indi == null ? null : indi.father; Individual Mother = indi == null ? null : indi.mother; if(currentGen != maxGensThatFit){ drawPersonLine(session, baseX + (boxWidth * 11.0/10.0), baseY + (heightLeft / 4.0 ), (heightLeft / 2.0 ), currentGen+1, Father, Mother, fontGenSizes); drawPersonLine(session, baseX + (boxWidth * 11.0/10.0), baseY - (heightLeft / 4.0 ), (heightLeft / 2.0 ), currentGen+1, Mother, null, fontGenSizes); } } } protected String PlaceAbbr(OpgSession session, String place, double width, float fontSize) { if (place != null) { return PlaceAbbreviator.placeFit(place, (float)(boxWidth - width), session.getOpgOptions().getFont().font.deriveFont(fontSize)); } else return ""; } protected String[] getMarriageDateAndPlace(Individual indi, Individual mother) { String[] retValue = new String[3]; // If there is more then one spouse we need to add the correct spouse // to connect to the person's genealogy. Search through the tree. if (indi != null && mother != null && indi.fams.size() > 1) { int motherID = -1; // Search for the correct mom for (int indexMom = 0; indexMom < indi.fams.size(); indexMom++) { // If we find the mom then we are done. if (indi.fams.get(indexMom).wifeId.equals(mother.id)) { motherID = indexMom; // Stop our search indexMom = indi.fams.size(); } } // Set the strings accordingly retValue[0] = (motherID == -1) ? null : ((indi.fams.get(motherID).marriage == null || indi.fams.get(motherID).marriage.date == null) ? "" : indi.fams.get(motherID).marriage.date); retValue[1] = (motherID == -1) ? null : ((indi.fams.get(motherID).marriage == null || indi.fams.get(motherID).marriage.place == null) ? "" : indi.fams.get(motherID).marriage.place); retValue[2] = (motherID == -1) ? null : ((indi.fams.get(motherID).divorce) ? "(D)" : ""); } // If there was only one spouse then we know that (s)he is the correct one else if (indi != null && mother == null && indi.fams.size() > 1) { //Individual Spouse = (indi.id != indi.fams.get(0).husband.id) ? indi.fams.get(0).husband : indi.fams.get(0).wife; retValue[0] = (indi.fams.get(0).marriage == null || indi.fams.get(0).marriage.date == null) ? "" : indi.fams.get(0).marriage.date; retValue[1] = (indi.fams.get(0).marriage == null || indi.fams.get(0).marriage.place == null) ? "" : indi.fams.get(0).marriage.place; retValue[2] = (indi.fams.get(0).divorce) ? "(D)" : ""; /* retValue[0] = (indi.fams.get(indi.fams.size() - 1).marriage == null || indi.fams.get(indi.fams.size() - 1).marriage.date == null) ? "" : indi.fams.get(indi.fams.size() - 1).marriage.date; retValue[1] = (indi.fams.get(indi.fams.size() - 1).marriage == null || indi.fams.get(indi.fams.size() - 1).marriage.place == null) ? "" : indi.fams.get(indi.fams.size() - 1).marriage.place;*/ } // If there was only one spouse then we know that (s)he is the correct one else if (indi != null && indi.fams.size() == 1) { //Individual Spouse = (indi.id != indi.fams.get(0).husband.id) ? indi.fams.get(0).husband : indi.fams.get(0).wife; retValue[0] = (indi.fams.get(0).marriage == null || indi.fams.get(0).marriage.date == null) ? "" : indi.fams.get(0).marriage.date; retValue[1] = (indi.fams.get(0).marriage == null || indi.fams.get(0).marriage.place == null) ? "" : indi.fams.get(0).marriage.place; retValue[2] = (indi.fams.get(0).divorce ? "(D)" : ""); } // No spouse then no Marriage Info else { retValue[0] = null; retValue[1] = null; } return retValue; } protected void drawLogo(double chartWidth, double chartHeight) { String strLogo = programLogo; chart.addDrawCommand(new DrawCmdSetFont(OpgFont.getDefaultSerifFont(Font.PLAIN, 12),Color.LIGHT_GRAY)); double width = OpgFont.getDefaultSerifFont(Font.PLAIN, 12).getStringBounds(strLogo, NameAbbreviator.frc).getWidth(); chart.addDrawCommand(new DrawCmdMoveTo((chartWidth - (width + 20)), 20)); chart.addDrawCommand(new DrawCmdText(strLogo)); } protected String getGenerationLabel(int gen) { switch (gen) { case 0: return root.givenName + " " + root.middleName + " " + root.surname;// 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) + " GGP"; else return (-gen)-2 + getOrdinalSuffix((-gen)-2) + " GGC"; } } 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 drawTitles() { //choose font size for generation labels FontRenderContext frc = NameAbbreviator.frc; String name = root.namePrefix + root.givenName + root.middleName + root.surname + root.nameSuffix; //draw each generation label Font font = OpgFont.getDefaultSansSerifFont(Font.BOLD, labelFontSize); LineMetrics lm = font.getLineMetrics("gjpqyjCAPSQJbdfhkl", frc); double horizPos = marginSize + (boxWidth * 11.0/10.0); double vertPos = (ops.isLandscape())? ops.getPaperWidth().width : ops.getPaperLength() - marginSize - headerSize + (2*lm.getLeading()) + lm.getDescent(); //draw ancestor labels chart.addDrawCommand(new DrawCmdSetFont(font, Color.RED)); for(int gen = 1; gen <= ops.getAncesGens(); ++gen) { double width = font.getStringBounds(getGenerationLabel(gen), frc).getWidth(); //draw label centered above boxes for generation chart.addDrawCommand(new DrawCmdMoveTo(horizPos + (boxWidth - width)/2.0, vertPos )); chart.addDrawCommand(new DrawCmdText(getGenerationLabel(gen))); horizPos += boxWidth * 11.0/10.0; } //draw individual label double width = font.getStringBounds(name, frc).getWidth(); chart.addDrawCommand(new DrawCmdMoveTo(marginSize + (boxWidth - width)/2.0, vertPos)); chart.addDrawCommand(new DrawCmdText(name)); } public LinkedList<ShapeInfo> getChartShapes() { // Auto-generated method stub return null; } @Override public ShapeInfo getIndiIntersect(double x, double y, int maxAnces, int maxDesc, OpgSession session) { return null; } @Override public LinkedList<ShapeInfo> getChartShapes(int maxAnces, int maxDesc, OpgSession session) { return null; } @Override public void setChartStyle(StylingBoxScheme style) { } @Override public StylingBoxScheme getBoxStyles() { return null; } public ArrayList<ImageFile> getImages(){ return images; } @Override public void setIsPrimaryMaker(boolean set) { isPrimaryMaker = set; } }