/**
* FyLLGen - A Java based tool for collecting and distributing family data
*
* Copyright (C) 2007-2011 Christian Packenius
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.chris_soft.fyllgen.widget;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import de.chris_soft.fyllgen.data.Family;
import de.chris_soft.fyllgen.data.OptionData;
import de.chris_soft.fyllgen.data.Person;
import de.chris_soft.fyllgen.data.Relationship;
import de.chris_soft.fyllgen.data.RelationshipParentChild;
import de.chris_soft.fyllgen.data.RelationshipPartners;
import de.chris_soft.fyllgen.utilities.SwtConsts;
/**
* Hier k�mmert man sich lediglich um das Zeichnen der Verbindungslinien
* zwischen den dargestellten Personen.
* @author Christian Packenius, Juli 2008.
*/
public class FamilyCompositePainter implements PaintListener {
/**
* Liste aller darzustellenden Personen.
*/
private final List<Person> listAllPersons;
/**
* Liste aller Widgets zu den darzustellenden Personen.
*/
private final List<PersonView> listAllPersonComposites;
/**
* Aktuelle Person.
*/
private Person currentMainPerson;
/**
* Konstruktor.
* @param listAllPersons Liste aller darzustellenden Personen.
* @param listAllPersonComposites Zu jeder darzustellenden Person das
* entsprechende Widget.
*/
public FamilyCompositePainter(List<Person> listAllPersons, List<PersonView> listAllPersonComposites) {
this.listAllPersons = listAllPersons;
this.listAllPersonComposites = listAllPersonComposites;
}
/**
* Alle bestehenden Verbindungen zwischen Personen einzeichnen.
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(PaintEvent e) {
// Erst alles in ein Image malen und dann in einem Zug in das Control
// zeichnen.
GC egc = e.gc;
Control widget = (Control) e.widget;
Point size = widget.computeSize(SWT.DEFAULT, SWT.DEFAULT);
Image image = new Image(Display.getDefault(), size.x, size.y);
GC gc = new GC(image);
gc.setBackground(widget.getBackground());
gc.fillRectangle(0, 0, size.x, size.y);
currentMainPerson = Family.instance.getCurrentPerson();
// Zu allen Personen Partner und Kinder anzeigen.
for (Person person : listAllPersons) {
// Alle Kinder anzeigen.
Person[] children = person.getChildren();
int childrenLength = children.length;
for (int i = 0; i < childrenLength; i++) {
Person child = children[i];
PersonView pc1 = listAllPersonComposites.get(listAllPersons.indexOf(person));
int childIndex = listAllPersons.indexOf(child);
if (childIndex >= 0) {
PersonView pc2 = listAllPersonComposites.get(childIndex);
drawConnection(gc, pc1, pc2);
}
}
// Alle Partner anzeigen.
Person[] partners = person.getPartner();
for (Person partner : partners) {
PersonView pc1 = listAllPersonComposites.get(listAllPersons.indexOf(person));
int partnerIndex = listAllPersons.indexOf(partner);
if (partnerIndex >= 0) {
PersonView pc2 = listAllPersonComposites.get(partnerIndex);
drawConnection(gc, pc1, pc2);
}
}
}
gc.dispose();
egc.drawImage(image, 0, 0);
image.dispose();
}
/**
* Zeichnet die Verbindung zwischen den beiden Composite-Objekten ein.
*/
private void drawConnection(GC gc, PersonView pc1, PersonView pc2) {
// Farben holen und allokieren.
Color colorLineCurrentPerson = OptionData.instance.getColor(OptionData.COLOR_LINE_CURRENT_PERSON);
Color colorLineOtherPerson = OptionData.instance.getColor(OptionData.COLOR_LINE_OTHER_PERSON);
// Bei einer Ehe und einem leiblichen Kind wird die Linie durchgezeichnet,
// sonst gestrichelt.
Relationship relship = pc1.person.getRelationship(pc2.person);
boolean bFullLine = relship.getValue(Relationship.TYPE, "option").equals(RelationshipPartners.MARRIAGE);
bFullLine |= relship.getValue(Relationship.TYPE, "option").equals(RelationshipParentChild.BIOLOGICAL);
// Grenzen der beiden Composites holen, die miteinander verbunden werden
// sollen.
Rectangle rc1 = pc1.getBounds();
Rectangle rc2 = pc2.getBounds();
// Die aktuelle Person sowie die ihr direkt zugeordneten blau zeichnen, die
// anderen normal schwarz.
if (pc1.person == currentMainPerson || pc2.person == currentMainPerson) {
gc.setForeground(colorLineCurrentPerson);
}
else {
gc.setForeground(colorLineOtherPerson);
}
// Strichart setzen.
if (bFullLine) {
gc.setLineStyle(SWT.LINE_SOLID);
}
else {
gc.setLineStyle(SWT.LINE_DASH);
}
// Beziehungslinienstil setzen.
String sLineStyle = Integer.toString(SWT.LINE_SOLID);
String relType = relship.getType();
if (relship instanceof RelationshipPartners) {
if (relType.equals(RelationshipPartners.MARRIAGE)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARTNER_MARRIAGE);
}
else if (relType.equals(RelationshipPartners.ENGAGEMENT)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARTNER_ENGAGEMENT);
}
else if (relType.equals(RelationshipPartners.DIVORCE)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARTNER_DIVORCE);
}
else if (relType.equals(RelationshipPartners.FRIENDSHIP)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARTNER_FRIENDSHIP);
}
}
else if (relship instanceof RelationshipParentChild) {
if (relType.equals(RelationshipParentChild.BIOLOGICAL)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARENT_CHILD_BIOLOGICAL);
}
else if (relType.equals(RelationshipParentChild.ADOPTIVE)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARENT_CHILD_ADOPTIVE);
}
else if (relType.equals(RelationshipParentChild.STEP)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARENT_CHILD_STEP);
}
else if (relType.equals(RelationshipParentChild.FOSTER)) {
sLineStyle = OptionData.instance.getComboKey(OptionData.LINE_TYPE_PARENT_CHILD_FOSTER);
}
}
gc.setLineStyle(Integer.parseInt(sLineStyle));
// Vom ersten Composite rechts Mitte, vom zweiten links Mitte.
Point p1 = new Point(rc1.x + rc1.width, rc1.y + rc1.height / 2);
Point p2 = new Point(rc2.x, rc2.y + rc2.height / 2);
// Falls die Composites teilweise untereinander stehen, an der rechten Seite
// verbinden.
if (p1.x >= rc2.x) {
drawPartnersLine(gc, p1, p2, rc1, rc2);
}
else {
drawParentChildLine(gc, p1, p2);
}
// Farben wieder freigeben.
colorLineCurrentPerson.dispose();
colorLineOtherPerson.dispose();
}
/**
* Zeichnet eine Linie zwischen zwei Partnern.
*/
private void drawPartnersLine(GC gc, Point p1, Point p2, Rectangle rc1, Rectangle rc2) {
// p2 soll nun unter p1 sein!
if (p1.y > p2.y) {
Point ph = p1;
p1 = p2;
p2 = ph;
}
// TODO - Je nach Art der Partnerschaft stricheln oder punkten. Beispiel:
// Ehe: Durchgezogen.
// Geschieden: Gepunktet.
// Der zweite Punkt soll dann auch rechts Mitte liegen.
p2 = new Point(rc2.x + rc2.width, rc2.y + rc2.height / 2);
int maxX = Math.max(p1.x, p2.x) + 10;
int dx1 = maxX - p1.x;
int dx2 = maxX - p2.x;
int dy = p2.y - p1.y;
if (OptionData.instance.getBoolean(OptionData.DRAW_STRAIGHT_LINES_FOR_PARTNERS)) {
gc.drawLine(p1.x, p1.y, p1.x + dx1, p1.y);
gc.drawLine(p1.x + dx1, p1.y, p1.x + dx1, p2.y);
gc.drawLine(p2.x, p2.y, p1.x + dx1, p2.y);
}
else {
gc.drawArc(p1.x - dx1, p1.y, dx1 + dx1, dy, 0, 90);
gc.drawArc(p2.x - dx2, p1.y, dx2 + dx2, dy, 270, 90);
}
}
/**
* Zeichnet eine Linie vom Elternteil zum Kind.
*/
private void drawParentChildLine(GC gc, Point p1, Point p2) {
// Nun von links nach rechts verbinden.
// pc1 muss links von pc2 sein!
// Au�erdem sollten alle PersonComposites gleich gro� sein!
if (p1.x > p2.x) {
Point ph = p1;
p1 = p2;
p2 = ph;
}
// TODO - Per Option eine andere Darstellung erlauben:
// - Biologische Eltern oder Adoptiveltern nicht jeweils mit einer Linie
// versehen, sondern mit der Partnerschaftslinie
// verbinden.
// - Stief- oder Pflegeeltern "normal" mit einzelner Linie anzeigen,
// allerdings gepunktet (Pflegeeltern) oder
// gestrichelt
// (Stiefeltern).
if (Math.abs(p1.y - p2.y) < 6 || OptionData.instance.getBoolean(OptionData.DRAW_STRAIGHT_LINES_FOR_CHILDREN)) {
gc.drawLine(p1.x, p1.y, p2.x, p2.y);
}
else {
int width = p2.x - p1.x;
// Ersetzen der Kurven unten? Versuch!
Path path = new Path(SwtConsts.display);
path.moveTo(p1.x, p1.y);
path.cubicTo(p1.x + width / 2, p1.y, p2.x - width / 2, p2.y, p2.x, p2.y);
gc.drawPath(path);
path.dispose();
}
}
}