edu.umb.jsPedigrees.client.Pelican.Pelican.java Source code

Java tutorial

Introduction

Here is the source code for edu.umb.jsPedigrees.client.Pelican.Pelican.java

Source

package edu.umb.jsPedigrees.client.Pelican;
/********************************************************************
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA  02111-1307, USA.
 *
 *  @author Copyright (C) Frank Dudbridge
 *  modified 2012 Brian White
 *
 ********************************************************************/

//package uk.ac.mrc.rfcgr;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Vector;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;

import edu.umb.jsPedigrees.client.JsPedigrees;
import edu.umb.jsPedigrees.client.PE.GenotypeSet;
import edu.umb.jsPedigrees.client.PE.PedigreeSolution;
import edu.umb.jsPedigrees.client.PE.PedigreeSolver;

public class Pelican extends AbsolutePanel implements ClickHandler {

    public static int PEDIGREE_SIZE = 600;
    public static String PEDIGREE_SIZE_STR = String.valueOf(PEDIGREE_SIZE + "px");

    public static MenuBar menuBar;
    private MenuItem undoItem;
    private MenuItem redoItem;
    private PopupPanel popup;
    private PelicanPerson currentPerson;
    private int currentId;
    private boolean pedHasChanged;
    private MenuItem Parents;
    private Vector<Vector<PelicanPerson>> history;
    private int historyPosition;

    private HashSet<String> matingList;

    /*
     * array for determining where the user has activated the context menu
     *    (right-clicked)
     * 
     * it is pixel-for-pixel the drawing canvas
     * 
     * background = 0
     * if a PelicanPerson is there, then the cells under that person have his/her id#
     */
    private int[][] screen;

    /* {{{ constructor (popup menu) */

    public Pelican(RootPanel rootPanel) {
        super();
        setSize(PEDIGREE_SIZE_STR, PEDIGREE_SIZE_STR);
        setStyleName("jsPX-canvas");

        makeMenus(rootPanel);

        history = new Vector<Vector<PelicanPerson>>();
        historyPosition = 0;
        matingList = new HashSet<String>();

        addDomHandler(this, ClickEvent.getType());

        exportMethods(this);

        Scheduler.get().scheduleDeferred(new Command() {
            public void execute() {
                notifyHostpage();
            }
        });
    }

    public static native void notifyHostpage() /*-{
                                               if (typeof $wnd.pedexIsReady === 'function') 
                                               $wnd.pedexIsReady();
                                               }-*/;

    /*
     * methods for interfacing with js pages
     */
    public static native void exportMethods(Pelican p) /*-{
                                                       // methods for page buttons
                                                       $wnd.pedexNewPedigree = $entry(function() {return p.@edu.umb.jsPedigrees.client.Pelican.Pelican::newPedigree()();});
                                                       $wnd.pedexSolvePedigree = $entry(function() {return p.@edu.umb.jsPedigrees.client.Pelican.Pelican::solvePedigree()();});
                                                           
                                                       // methods for LMS interfacing
                                                       $wnd.getStateXML = $entry(function() {return p.@edu.umb.jsPedigrees.client.Pelican.Pelican::getStateXML()();});
                                                       $wnd.setStateXML = $entry(function(xmlString) {return p.@edu.umb.jsPedigrees.client.Pelican.Pelican::setStateXML(Ljava/lang/String;)(xmlString);});
                                                       $wnd.getGradeXML = $entry(function() {return p.@edu.umb.jsPedigrees.client.Pelican.Pelican::getGradeXML()();});
                                                       }-*/;

    public String getStateXML() {
        return getState();
    }

    public void setStateXML(String state) {
        setState(state);
    }

    public String getGradeXML() {
        return getGrade();
    }

    private void makeMenus(RootPanel rootPanel) {
        // set up the popup menu that appears when you click on a person
        popup = new PopupPanel();

        MenuBar personMenu = new MenuBar(true);

        MenuBar addMenu = new MenuBar(true);
        addMenu.addItem("1 son", new Command() {
            public void execute() {
                addChildren("1 son");
                popup.hide();
            }
        });
        addMenu.addItem("1 daughter", new Command() {
            public void execute() {
                addChildren("1 daughter");
                popup.hide();
            }
        });
        addMenu.addItem("2 sons", new Command() {
            public void execute() {
                addChildren("2 sons");
                popup.hide();
            }
        });
        addMenu.addItem("2 daughters", new Command() {
            public void execute() {
                addChildren("2 daughters");
                popup.hide();
            }
        });
        addMenu.addItem("3 sons", new Command() {
            public void execute() {
                addChildren("3 sons");
                popup.hide();
            }
        });
        addMenu.addItem("3 daughters", new Command() {
            public void execute() {
                addChildren("3 daughters");
                popup.hide();
            }
        });
        addMenu.addItem("Spouse+son", new Command() {
            public void execute() {
                addSpouse("Spouse+son");
                popup.hide();
            }
        });
        addMenu.addItem("Spouse+daughter", new Command() {
            public void execute() {
                addSpouse("Spouse+daughter");
                popup.hide();
            }
        });
        Parents = new MenuItem("Parents", new Command() {
            public void execute() {
                addParents();
                popup.hide();
            }
        });
        addMenu.addItem(Parents);
        personMenu.addItem("Add", addMenu);

        MenuBar changeMenu = new MenuBar(true);

        MenuBar changeAff = new MenuBar(true);
        changeAff.addItem("Affected", new Command() {
            public void execute() {
                currentPerson.affection = PelicanPerson.affected;
                updateDisplay();
                popup.hide();
            }
        });
        changeAff.addItem("Unaffected", new Command() {
            public void execute() {
                currentPerson.affection = PelicanPerson.unaffected;
                updateDisplay();
                popup.hide();
            }
        });
        changeMenu.addItem("Affection", changeAff);

        MenuBar changeSex = new MenuBar(true);
        changeSex.addItem("Male", new Command() {
            public void execute() {
                currentPerson.sex = PelicanPerson.male;
                updateDisplay();
                popup.hide();
            }
        });
        changeSex.addItem("Female", new Command() {
            public void execute() {
                currentPerson.sex = PelicanPerson.female;
                updateDisplay();
                popup.hide();
            }
        });
        changeMenu.addItem("Sex", changeSex);

        personMenu.addItem("Change", changeMenu);

        personMenu.addItem("Delete", new Command() {
            public void execute() {
                deletePerson(currentPerson);
                popup.hide();
            }
        });

        personMenu.addItem("Cancel", new Command() {
            public void execute() {
                popup.hide();
            }
        });

        MenuBar popupMenu = new MenuBar();
        popupMenu.addItem("Edit", personMenu);
        popupMenu.setAutoOpen(true);
        popup.add(popupMenu);

        // main menu
        MenuBar mainMenu = new MenuBar();
        mainMenu.setWidth("50px");
        mainMenu.setAnimationEnabled(true);

        MenuBar editMenu = new MenuBar(true);
        editMenu.setAnimationEnabled(true);

        editMenu.addItem("New Pedigree", new Command() {
            public void execute() {
                newPedigree();
                updateDisplay();
            }
        });
        undoItem = new MenuItem("Undo", new Command() {
            public void execute() {
                if (historyPosition > 1) {
                    historyPosition--;
                    Vector<PelicanPerson> savedPed = (Vector<PelicanPerson>) history.elementAt(historyPosition - 1);
                    loadPedigree(savedPed);
                    pedHasChanged = true;
                    updateDisplay();
                }
            }
        });
        editMenu.addItem(undoItem);
        redoItem = new MenuItem("Redo", new Command() {
            public void execute() {
                if (historyPosition < history.size()) {
                    historyPosition++;
                    Vector<PelicanPerson> savedPed = (Vector<PelicanPerson>) history.elementAt(historyPosition - 1);
                    loadPedigree(savedPed);
                    pedHasChanged = true;
                    updateDisplay();
                }
            }
        });
        editMenu.addItem(redoItem);
        editMenu.addItem("Renumber", new Command() {
            public void execute() {
                renumberAll();
                updateDisplay();
            }
        });
        mainMenu.addItem(new MenuItem("Edit", editMenu));

        rootPanel.add(mainMenu);

    }

    // get state for LMS in XML format as a string
    public String getState() {
        savePedigree();
        return getXMLDoc().toString();
    }

    public void setState(String state) {
        // read in this pedigree from the xml
        Vector<PelicanPerson> newPedigree = new Vector<PelicanPerson>();
        int pedSize = 0;
        Vector<Integer> pidList = new Vector<Integer>();
        Vector<Integer> midList = new Vector<Integer>();
        HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();

        Document doc = XMLParser.parse(state);
        NodeList people = doc.getDocumentElement().getChildNodes();
        for (int i = 0; i < people.getLength(); i++) {
            Element e = (Element) people.item(i);
            if (e.getTagName().equals("Person")) {
                int id = Integer.parseInt(e.getAttribute("Id"));
                int sex = Integer.parseInt(e.getAttribute("Sex"));
                int affection = Integer.parseInt(e.getAttribute("Affection"));

                String[] genotype = new String[2];
                genotype[0] = "?";
                genotype[1] = "?";

                PelicanPerson person = new PelicanPerson(this, id, null, null, sex, affection, "", 0, genotype);
                newPedigree.add(person);

                int father = Integer.parseInt(e.getAttribute("Father"));
                pidList.add(new Integer(father));
                int mother = Integer.parseInt(e.getAttribute("Mother"));
                midList.add(new Integer(mother));

                idMap.put(new Integer(id), new Integer(pedSize++));

            }
        }

        for (int i = 0; i < newPedigree.size(); i++) {
            PelicanPerson person = (PelicanPerson) newPedigree.elementAt(i);
            //gww       if (((Integer)pidList.elementAt(i)).intValue()!=PelicanPerson.unknown) {
            if ((Integer) pidList.elementAt(i) != PelicanPerson.unknownID) {
                if (!idMap.containsKey(pidList.elementAt(i)))
                    //gww          throw(new Error("Father of subject "+String.valueOf(person.id)+" is missing"));
                    throw (new Error("Father of subject " + person.id + " is missing"));
                person.father = (PelicanPerson) newPedigree
                        .elementAt(((Integer) idMap.get(pidList.elementAt(i))).intValue());
            }
            //gww       if (((Integer)midList.elementAt(i)).intValue()!=PelicanPerson.unknown) {
            if ((Integer) midList.elementAt(i) != PelicanPerson.unknownID) {
                if (!idMap.containsKey(midList.elementAt(i)))
                    //gww          throw(new Error("Mother of subject "+String.valueOf(person.id)+" is missing"));
                    throw (new Error("Mother of subject " + person.id + " is missing"));
                person.mother = (PelicanPerson) newPedigree
                        .elementAt(((Integer) idMap.get(midList.elementAt(i))).intValue());
            }
        }

        // figure out the generations
        ((PelicanPerson) newPedigree.elementAt(0)).laidOut = true;
        boolean someChange = true;
        int nperson = newPedigree.size();
        // repeatedly pass through the pedigree all subjects laid out
        while (someChange) {
            someChange = false;
            for (int i = 0; i < nperson; i++) {
                PelicanPerson p = (PelicanPerson) newPedigree.elementAt(i);
                if (!p.laidOut) {
                    // try to get it from the parents
                    for (int j = 0; j < nperson; j++) {
                        PelicanPerson parent = (PelicanPerson) newPedigree.elementAt(j);
                        if (parent == p.father && parent.laidOut) {
                            p.generation = parent.generation + 1;
                            p.laidOut = true;
                            someChange = true;
                        }
                        if (parent == p.mother && parent.laidOut) {
                            p.generation = parent.generation + 1;
                            p.laidOut = true;
                            someChange = true;
                        }
                    }
                }
                if (p.laidOut) {
                    // assign parents generation
                    for (int j = 0; j < nperson; j++) {
                        PelicanPerson parent = (PelicanPerson) newPedigree.elementAt(j);
                        if (parent == p.father && !parent.laidOut) {
                            parent.generation = p.generation - 1;
                            parent.laidOut = true;
                            someChange = true;
                        }
                        if (parent == p.mother && !parent.laidOut) {
                            parent.generation = p.generation - 1;
                            parent.laidOut = true;
                            someChange = true;
                        }
                    }
                }
            }
        }

        if (!checkIntegrity(newPedigree).equals("")) {
            return;
        }

        // end
        clear();
        currentId = 0;
        for (int i = 0; i < nperson; i++) {
            PelicanPerson p = (PelicanPerson) newPedigree.elementAt(i);
            add(p);
            //gww need to add in check for valid integer ID else ignore updating of currentId
            if (p.id > 0) {
                //gww           if (i==0 || p.id>currentId) currentId=p.id;
                if (i == 0 || p.id > currentId)
                    currentId = p.id;
            }
        }
        currentId++;
        newPedigree.removeAllElements();
        updateDisplay();
    }

    public String getGrade() {
        Document d = XMLParser.createDocument();
        Element root = d.createElement("Grade");

        // first, some specifications of the pedigree
        int numMales = 0;
        int numFemales = 0;
        int numAffectedMales = 0;
        int numAffectedFemales = 0;
        PelicanPerson[] people = getAllPeople();
        for (int i = 0; i < people.length; i++) {
            PelicanPerson p = people[i];
            if (p.sex == PelicanPerson.male) {
                numMales++;
                if (p.affection == PelicanPerson.affected) {
                    numAffectedMales++;
                }
            } else {
                numFemales++;
                if (p.affection == PelicanPerson.affected) {
                    numAffectedFemales++;
                }
            }
        }
        Element e = d.createElement("Counts");

        Element M = d.createElement("M");
        M.appendChild(d.createTextNode(String.valueOf(numMales)));
        e.appendChild(M);

        Element F = d.createElement("F");
        F.appendChild(d.createTextNode(String.valueOf(numFemales)));
        e.appendChild(F);

        Element aM = d.createElement("aM");
        aM.appendChild(d.createTextNode(String.valueOf(numAffectedMales)));
        e.appendChild(aM);

        Element aF = d.createElement("aF");
        aF.appendChild(d.createTextNode(String.valueOf(numAffectedFemales)));
        e.appendChild(aF);

        root.appendChild(e);

        // now, the analysis results
        renumberAll();
        updateDisplay();

        String integrity = checkIntegrity(history.lastElement());
        if (integrity.equals("")) {
            PedigreeSolver ps = new PedigreeSolver(getAllPeople(), getMatingList());
            PedigreeSolution sol = ps.solve();
            e = d.createElement("Analysis");

            GenotypeSet[] results = sol.getResults();

            Element AR = d.createElement("AR");
            if (results[0].getAll().size() > 0) {
                AR.appendChild(d.createTextNode("Y"));
            } else {
                AR.appendChild(d.createTextNode("N"));
            }
            e.appendChild(AR);

            Element AD = d.createElement("AD");
            if (results[1].getAll().size() > 0) {
                AD.appendChild(d.createTextNode("Y"));
            } else {
                AD.appendChild(d.createTextNode("N"));
            }
            e.appendChild(AD);

            Element SR = d.createElement("SR");
            if (results[2].getAll().size() > 0) {
                SR.appendChild(d.createTextNode("Y"));
            } else {
                SR.appendChild(d.createTextNode("N"));
            }
            e.appendChild(SR);

            Element SD = d.createElement("SD");
            if (results[3].getAll().size() > 0) {
                SD.appendChild(d.createTextNode("Y"));
            } else {
                SD.appendChild(d.createTextNode("N"));
            }
            e.appendChild(SD);
        }
        root.appendChild(e);

        return root.toString();
    }

    private Element getXMLDoc() {
        Document d = XMLParser.createDocument();
        Element root = d.createElement("PedEx");
        PelicanPerson[] people = getAllPeople();
        for (int i = 0; i < people.length; i++) {
            root.appendChild(people[i].save());
        }
        return root;
    }

    private void newPedigree() {
        // start out with a single female
        clear();
        currentId = 1;
        add(new PelicanPerson(this, currentId++, PelicanPerson.female, 0));
        savePedigree();
        pedHasChanged = true;
        updateDisplay();
    }

    // save current pedigree in the history, and clear the future
    private void savePedigree() {
        Vector<PelicanPerson> savedPed = new Vector<PelicanPerson>();
        HashMap<Integer, PelicanPerson> idMap = new HashMap<Integer, PelicanPerson>();
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson p = (PelicanPerson) getWidget(i);
                savedPed.add(new PelicanPerson(p));
                idMap.put(new Integer(p.id), (PelicanPerson) savedPed.lastElement());
            }
        for (int i = 0; i < savedPed.size(); i++) {
            PelicanPerson p = (PelicanPerson) savedPed.elementAt(i);
            if (p.father != null)
                p.father = (PelicanPerson) idMap.get(p.father.id);
            if (p.mother != null)
                p.mother = (PelicanPerson) idMap.get(p.mother.id);
        }
        while (history.size() > historyPosition)
            history.remove(history.lastElement());
        history.add(savedPed);
        historyPosition++;
    }

    // load pedigree from the history
    private void loadPedigree(Vector<PelicanPerson> savedPed) {
        clear();
        HashMap<Integer, PelicanPerson> idMap = new HashMap<Integer, PelicanPerson>();
        for (int i = 0; i < savedPed.size(); i++) {
            PelicanPerson p = (PelicanPerson) savedPed.elementAt(i);
            PelicanPerson person = new PelicanPerson(p);
            add(person);
            idMap.put(new Integer(p.id), person);
        }
        for (int i = 0; i < getWidgetCount(); i++) {
            PelicanPerson p = (PelicanPerson) getWidget(i);
            if (p.father != null)
                //gww      p.father=(PelicanPerson)idMap.get(new Integer(p.father.id));
                p.father = (PelicanPerson) idMap.get(p.father.id);
            if (p.mother != null)
                //gww      p.mother=(PelicanPerson)idMap.get(new Integer(p.mother.id));
                p.mother = (PelicanPerson) idMap.get(p.mother.id);
        }
        updateDisplay();
    }

    /* }}} */

    /* {{{ updateDisplay */

    public void updateDisplay() {
        savePedigree();
        pedHasChanged = true;
        drawPedigree();
    }

    /* }}} */

    /* {{{ areSpouses */

    private boolean areSpouses(PelicanPerson person1, PelicanPerson person2) {
        if (person1 == null || person2 == null)
            return (false);
        // construct unique set entry from the id strings concatenated with a space
        if (matingList.contains(person1.id + " " + person2.id)
                || matingList.contains(person2.id + " " + person1.id))
            return (true);
        return (false);
    }

    /* }}} */

    /* {{{ isChild */

    private boolean isChild(PelicanPerson child, PelicanPerson person1, PelicanPerson person2) {
        if (child.father == person1 && child.mother == person2
                || child.mother == person1 && child.father == person2)
            return (true);
        return (false);
    }

    /* }}} */

    /* {{{ areSibs */

    private boolean areSibs(PelicanPerson person1, PelicanPerson person2) {
        if (person1.father == person2.father && person1.mother == person2.mother)
            return (true);
        return (false);
    }

    /* }}} */

    /* {{{ isAncestor */

    private boolean isAncestor(PelicanPerson parent, PelicanPerson child) {
        if (child.father == parent || child.mother == parent)
            return (true);
        if (child.father != null) {
            if (isAncestor(parent, child.father))
                return (true);
        }
        if (child.mother != null) {
            if (isAncestor(parent, child.mother))
                return (true);
        }
        return (false);
    }

    /* }}} */

    /* {{{ addParents */

    private void addParents() {
        // new father
        currentPerson.father = new PelicanPerson(this, currentId++, PelicanPerson.male,
                currentPerson.generation - 1);
        add(currentPerson.father);
        // new mother
        currentPerson.mother = new PelicanPerson(this, currentId++, PelicanPerson.female,
                currentPerson.generation - 1);
        add(currentPerson.mother);

        updateDisplay();
    }

    /* {{{ addChildren */

    private void addChildren(String request) {

        // find spouse for this subject
        PelicanPerson spouse = null;
        for (int i = 0; i < getWidgetCount() && spouse == null; i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson person = (PelicanPerson) getWidget(i);
                if (areSpouses(person, currentPerson))
                    spouse = person;
            }
        // create a spouse
        if (spouse == null) {
            if (currentPerson.sex == PelicanPerson.female)
                spouse = new PelicanPerson(this, currentId++, PelicanPerson.male, currentPerson.generation);
            else
                spouse = new PelicanPerson(this, currentId++, PelicanPerson.female, currentPerson.generation);
            add(spouse);
        }

        // add in the children
        int generation = Math.max(currentPerson.generation, spouse.generation);
        for (int i = 0; i < Integer.parseInt(request.substring(0, 1)); i++) {
            if (currentPerson.sex == PelicanPerson.female) {
                if (request.charAt(2) == 's')
                    add(new PelicanPerson(this, currentId++, spouse, currentPerson, PelicanPerson.male,
                            generation + 1));
                else
                    add(new PelicanPerson(this, currentId++, spouse, currentPerson, PelicanPerson.female,
                            generation + 1));
            } else {
                if (request.charAt(2) == 's')
                    add(new PelicanPerson(this, currentId++, currentPerson, spouse, PelicanPerson.male,
                            generation + 1));
                else
                    add(new PelicanPerson(this, currentId++, currentPerson, spouse, PelicanPerson.female,
                            generation + 1));
            }
        }

        updateDisplay();
    }

    /* }}} */

    /* {{{ addSpouse */

    private void addSpouse(String request) {

        // create a spouse
        int spouseId = currentId++;
        PelicanPerson spouse;
        if (currentPerson.sex == PelicanPerson.female) {
            spouse = new PelicanPerson(this, spouseId, PelicanPerson.male, currentPerson.generation);
            add(spouse);
        } else {
            spouse = new PelicanPerson(this, spouseId, PelicanPerson.female, currentPerson.generation);
            add(spouse);
        }
        // add in the child
        if (currentPerson.sex == PelicanPerson.female) {
            if (request.charAt(7) == 's')
                add(new PelicanPerson(this, currentId++, spouse, currentPerson, PelicanPerson.male,
                        currentPerson.generation + 1));
            else
                add(new PelicanPerson(this, currentId++, spouse, currentPerson, PelicanPerson.female,
                        currentPerson.generation + 1));
        } else {
            if (request.charAt(7) == 's')
                add(new PelicanPerson(this, currentId++, currentPerson, spouse, PelicanPerson.male,
                        currentPerson.generation + 1));
            else
                add(new PelicanPerson(this, currentId++, currentPerson, spouse, PelicanPerson.female,
                        currentPerson.generation + 1));
        }

        updateDisplay();
    }

    /* }}} */

    /* {{{ changeId */

    // swaps the Id's for two subjects
    private void changeId(int oldId, int newId) {
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson person = (PelicanPerson) getWidget(i);
                if (person.id == oldId)
                    person.id = newId;
                else if (person.id == newId)
                    person.id = oldId;
            }
    }

    // renumber the subjects, top-down, left-right
    public void renumberAll() {
        boolean someChange = false;

        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson person = (PelicanPerson) getWidget(i);
                int thisId = 0;
                for (int j = 0; j < getWidgetCount(); j++)
                    if (getWidget(j) instanceof PelicanPerson) {
                        PelicanPerson person2 = (PelicanPerson) getWidget(j);
                        if (person2.generation < person.generation || person2.generation == person.generation
                                && getWidgetLeft(person2) <= getWidgetLeft(person))
                            thisId++;
                    }
                if (person.id != thisId) {
                    changeId(person.id, thisId);
                    someChange = true;
                }
            }
        if (someChange)
            updateDisplay();
    }

    /* {{{ deletePerson */

    // recursively mark a subject and its descendents for deletion
    private void markForDeletion(PelicanPerson person) {
        // remove all children
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson p = (PelicanPerson) getWidget(i);
                if (p.father == person || p.mother == person)
                    markForDeletion(p);
            }
        // remove orphan spouses, who have no other spouses of their own
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson p = (PelicanPerson) getWidget(i);
                if (areSpouses(p, person) && p.isOrphan()) {
                    boolean soleMate = true;
                    for (int j = 0; j < getWidgetCount(); j++)
                        if (getWidget(j) instanceof PelicanPerson) {
                            PelicanPerson pp = (PelicanPerson) getWidget(j);
                            if (areSpouses(p, pp) && pp != person)
                                soleMate = false;
                        }
                    if (soleMate)
                        p.laidOut = true;
                }
            }
        // remove this subject
        person.laidOut = true;
    }

    // main routine to delete a person
    private void deletePerson(PelicanPerson person) {
        // check if the person has children
        boolean hasChild = false;
        boolean hasPaternalSibs = false;
        boolean hasMaternalSibs = false;
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                PelicanPerson p = (PelicanPerson) getWidget(i);
                if (p.father == person || p.mother == person)
                    hasChild = true;
                if (p != person && p.father == person.father)
                    hasPaternalSibs = true;
                if (p != person && p.mother == person.mother)
                    hasMaternalSibs = true;
            }
        if (!hasChild && hasPaternalSibs && hasMaternalSibs) {
            remove(person);
            updateDisplay();
            return;
        }

        // here, laidOut indicates whether a subject is marked for deletion 
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson)
                ((PelicanPerson) getWidget(i)).laidOut = false;

        markForDeletion(person);
        if (person.hasMother())
            if (person.mother.isOrphan() && !hasMaternalSibs)
                person.mother.laidOut = true;
        if (person.hasFather())
            if (person.father.isOrphan() && !hasPaternalSibs)
                person.father.laidOut = true;

        boolean empty = true;
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                if (((PelicanPerson) getWidget(i)).laidOut) {
                    remove(i);
                    i--;
                } else
                    empty = false;
            }
        if (empty)
            newPedigree();
        updateDisplay();
    }

    private PelicanPerson getPersonById(int id) {
        PelicanPerson[] people = getAllPeople();
        for (int i = 0; i < people.length; i++) {
            if (people[i].id == id)
                return people[i];
        }
        return null;
    }

    /* {{{ layoutPerson */

    // recursively lay out a person
    // returns: number of horizontal units taken up by its nuclear family
    private double layoutPerson(PelicanPerson person, int across) {
        int offspringSpace = 0;
        int spouseSpace = 0;
        PelicanPerson spouse = null;
        int verticalOffset = 0;

        // lay out with the first spouse
        boolean haveSpouse = false;
        boolean hasSibs = false;
        for (int i = 0; i < getWidgetCount() && !haveSpouse; i++)
            if (getWidget(i) instanceof PelicanPerson) {
                spouse = (PelicanPerson) getWidget(i);
                PelicanPerson lastChild = null;
                if (areSpouses(spouse, person) /* && spouse.isOrphan() */ /*&&
                                                                          !spouse.laidOut*/) {
                    for (int j = 0; j < getWidgetCount(); j++)
                        if (getWidget(j) instanceof PelicanPerson) {
                            PelicanPerson child = (PelicanPerson) getWidget(j);
                            if (isChild(child, person, spouse)) {
                                offspringSpace += layoutPerson(child, across + offspringSpace);
                                lastChild = child;
                            }
                        }
                    haveSpouse = true;
                }
                if (areSibs(spouse, person) && spouse != person)
                    hasSibs = true;
            }
        // place to the left of the spouse
        if (!person.laidOut) {
            if (offspringSpace > 1) {
                setWidgetPosition(person, PelicanPerson.xSpace * (2 * across + offspringSpace - 2) / 2,
                        (PelicanPerson.ySpace + verticalOffset) * person.generation);
            } else {
                setWidgetPosition(person, PelicanPerson.xSpace * across,
                        (PelicanPerson.ySpace + verticalOffset) * person.generation);
            }
            // move a singleton child across by half a space
            if (!hasSibs && !haveSpouse) {
                if ((person.father == null || !person.father.laidOut)
                        && (person.mother == null || !person.mother.laidOut)) {
                    setWidgetPosition(person, getWidgetLeft(person) + PelicanPerson.xSpace / 2,
                            getWidgetTop(person));
                }
            }
            person.laidOut = true;
            spouseSpace++;
        }
        // place the spouse
        if (haveSpouse && !spouse.laidOut && spouse.generation == person.generation) {
            if (offspringSpace > 1) {
                setWidgetPosition(spouse,
                        PelicanPerson.xSpace * (2 * across + offspringSpace - 2 + spouseSpace * 2) / 2,
                        (PelicanPerson.ySpace + verticalOffset) * person.generation);
            } else {
                setWidgetPosition(spouse, PelicanPerson.xSpace * (across + spouseSpace),
                        (PelicanPerson.ySpace + verticalOffset) * person.generation);
            }
            spouse.laidOut = true;
            spouseSpace++;
        }

        int maxSpace = (offspringSpace > spouseSpace) ? offspringSpace : spouseSpace;

        // lay out with other spouses
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                spouse = (PelicanPerson) getWidget(i);
                spouseSpace = 0;
                offspringSpace = 0;
                if (areSpouses(spouse, person) && (spouse.isOrphan() || !spouse.laidOut)) {
                    for (int j = 0; j < getWidgetCount(); j++)
                        if (getWidget(j) instanceof PelicanPerson) {
                            PelicanPerson child = (PelicanPerson) getWidget(j);
                            if (isChild(child, person, spouse))
                                offspringSpace += layoutPerson(child, across + maxSpace + offspringSpace);
                        }
                    // place the spouse
                    if (!spouse.laidOut && spouse.generation == person.generation) {
                        setWidgetPosition(spouse,
                                PelicanPerson.xSpace * (2 * (across + maxSpace) + offspringSpace - 1) / 2,
                                (PelicanPerson.ySpace + verticalOffset) * person.generation);
                        spouse.laidOut = true;
                        spouseSpace++;
                    }
                    maxSpace += (offspringSpace > spouseSpace) ? offspringSpace : spouseSpace;
                }
            }

        // System.out.println("ID "+person.id+" max "+String.valueOf(maxSpace));

        return (maxSpace);
    }

    public PelicanPerson[] getAllPeople() {
        ArrayList<PelicanPerson> people = new ArrayList<PelicanPerson>();
        for (int i = 0; i < getWidgetCount(); i++) {
            if (getWidget(i) instanceof PelicanPerson) {
                people.add((PelicanPerson) getWidget(i));
            }
        }
        return people.toArray(new PelicanPerson[people.size()]);
    }

    public HashSet<String> getMatingList() {
        return matingList;
    }

    /* {{{ paintComponent: draw the pedigree */

    // draw the pedigree
    public void drawPedigree() {

        if (!pedHasChanged)
            return;
        undoItem.setEnabled(historyPosition > 1);
        redoItem.setEnabled(historyPosition != history.size());

        PelicanPerson[] people = getAllPeople();
        int numPeople = people.length;

        // Initialise: nobody laid out, orphans are root subjects
        for (int i = 0; i < numPeople; i++) {
            people[i].laidOut = false;
            people[i].root = people[i].isOrphan();
        }

        // Make list of matings and ensure roots have orphan spouses
        matingList.clear();
        for (int i = 0; i < numPeople; i++) {
            PelicanPerson person = people[i];
            if (person.father != null && person.mother != null)
                //gww                  matingList.add(new Dimension(person.father.id,person.mother.id));
                matingList.add(person.father.id + " " + person.mother.id);
            if (person.father != null && !person.father.isOrphan())
                if (person.mother != null && person.mother.generation >= person.father.generation)
                    person.mother.root = false;
            if (person.mother != null && !person.mother.isOrphan())
                if (person.father != null && person.father.generation >= person.mother.generation)
                    person.father.root = false;
        }

        // lay out the root subjects
        // person is a root if it has spouses which are all orphans
        int rootSpace = 0;
        for (int i = 0; i < numPeople; i++) {
            PelicanPerson person = people[i];
            if (!person.laidOut && person.isOrphan() && person.isRoot()) {
                rootSpace += layoutPerson(person, rootSpace);
            }
        }

        // normalise x-y locations
        for (int i = 0; i < getWidgetCount(); i++) {
            if (!(getWidget(i) instanceof PelicanPerson))
                remove(i);
        }
        int minx = 0;
        int miny = 0;
        int maxx = 0;
        int maxy = 0;
        for (int i = 0; i < getWidgetCount(); i++) {
            Widget c = getWidget(i);
            if (i == 0 || getWidgetLeft(c) < minx)
                minx = getWidgetLeft(c);
            if (i == 0 || getWidgetTop(c) < miny)
                miny = getWidgetTop(c);
            if (i == 0 || getWidgetLeft(c) > maxx)
                maxx = getWidgetLeft(c);
            if (i == 0 || getWidgetTop(c) > maxy)
                maxy = getWidgetTop(c);
        }

        // set up array to tell where clicks land
        screen = new int[PEDIGREE_SIZE][PEDIGREE_SIZE]; // initializes all to 0

        for (int i = 0; i < getWidgetCount(); i++) {
            Widget c = getWidget(i);
            // if pedigree fits in the frame, then centre it
            // otherwise start it at (0,0)
            int px, py;
            if (maxx - minx + PelicanPerson.xSpace < getOffsetWidth()) {
                px = getWidgetLeft(c) - (maxx + minx - getOffsetWidth() + PelicanPerson.symbolSize) / 2;
                py = getWidgetTop(c) - miny + PelicanPerson.symbolSize / 2;
            } else {
                px = getWidgetLeft(c) - minx + PelicanPerson.symbolSize / 2;
                py = getWidgetTop(c) - miny + PelicanPerson.symbolSize / 2;
            }
            setWidgetPosition(c, px, py);

            if (c instanceof PelicanPerson) {
                PelicanPerson p = (PelicanPerson) c;
                p.drawSymbol();
                for (int x = px; x < (px + p.symbolSize); x++) {
                    for (int y = py; y < (py + p.symbolSize); y++) {
                        screen[x][y] = p.id;
                    }
                }
            }
        }

        // draw the lines on the graph
        insert(PelicanLines.drawLines(this), 0, 0, 1);

        setSize(String.valueOf(maxx - minx + PelicanPerson.xSpace + PelicanPerson.symbolSize / 2),
                String.valueOf(maxy - miny + PelicanPerson.ySpace + PelicanPerson.symbolSize / 2));

        setVisible(true);

        currentId = 0;
        // need to check if this is a valid integer ID 
        for (int i = 0; i < getWidgetCount(); i++)
            if (getWidget(i) instanceof PelicanPerson) {
                // if 'id' is a valid integer
                int id = ((PelicanPerson) getWidget(i)).id;
                if (id > 0)
                    currentId = Math.max(currentId, ((PelicanPerson) getWidget(i)).id);
            }
        currentId++;
        currentPerson = null;
        pedHasChanged = false;
    }

    /* }}} */

    /* {{{ checkIntegrity */

    // check that fathers are male, etc
    private String checkIntegrity(Vector<PelicanPerson> pedigree) {
        StringBuffer b = new StringBuffer();

        for (int i = 0; i < pedigree.size(); i++) {
            PelicanPerson person = pedigree.elementAt(i);
            // some sanity checks to catch egregious errors that should never happen
            if ((person.mother == null) && (person.father != null)) {
                b.append("Error: Person #" + person.id + " has father but no mother!");
            }

            if ((person.mother != null) && (person.father == null)) {
                b.append("Error: Person #" + person.id + " has mother but no father!");
            }

            if (((person.mother != null) && (person.father != null))
                    && ((person.mother.genotype[0].equals("? ?")) || (person.father.genotype[0].equals("? ?")))) {
                b.append("Error: Person #" + person.id + " parent(s) genotypes have not been set!");
            }

            // tests from original Pelican
            boolean fatherError = false;
            boolean motherError = false;
            for (int j = 0; j < pedigree.size(); j++) {
                PelicanPerson parent = (PelicanPerson) pedigree.elementAt(j);
                if (parent == person.father && parent.sex == PelicanPerson.female)
                    fatherError = true;
                if (parent == person.mother && parent.sex == PelicanPerson.male)
                    motherError = true;
            }
            // perhaps the pid/mid fields are swapped?
            if (fatherError && motherError) {
                b.append("Subject " + person.id + " has a male mother and female father.\n");
            } else {
                if (fatherError) {
                    b.append("Subject " + person.id + " has a female father.\n");
                }
                if (motherError) {
                    b.append("Subject " + person.id + " has a male mother.\n");
                }
            }
        }
        return b.toString();
    }

    /**
     *
     * Popup menu listener
     *
     */
    public void onClick(ClickEvent event) {
        int x = event.getX();
        int y = event.getY();
        if (screen[x][y] != 0) {
            currentPerson = getPersonById(screen[x][y]);
            popup.showRelativeTo(currentPerson);
            popup.show();
        } else {
            popup.hide();
        }

    }

    public String solvePedigree() {
        renumberAll();
        updateDisplay();

        String integrity = checkIntegrity(history.lastElement());
        if (integrity.equals("")) {
            StringBuffer b = new StringBuffer();
            PedigreeSolver ps = new PedigreeSolver(getAllPeople(), getMatingList());
            PedigreeSolution sol = ps.solve();
            PedigreeSolution consolidatedSol = ps.consolidate(sol);
            b.append(consolidatedSol + "\n");
            return b.toString();
        } else {
            return integrity;
        }

    }
}