/*
* 05/02/2002 - 20:15:18
*
* ZGraphique -
* Copyright (C) 2002 Dreux Loic
* dreuxl@free.fr
*
* Modifications
* -------------
* Date : 2009 janvier 22
* @auteur : Bruno Dabo <bruno.dabo@lywoonsoftware.com>
* @objet : enlever les limites sur les vecteurs
*
* Date : 2009 mars 13
* @auteur : Benjamin Gandon
* @objet : ajouter la sélection multiple
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.analyse.core.gui.zgraph;
import java.awt.AWTEvent;
import java.awt.AWTEventMulticaster;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import javax.swing.JComponent;
import org.analyse.core.util.Constantes;
/**
* ZGraphique est un composant permettant d'afficher des éléments graphiques, de
* les déplacer, de les lier. <br>
* <br>
* Il est conseiller d'utiliser ce composant dans un <code>JScrollPane<code>.
*
*<pre>
* Container c = getContentPane();
* c.setLayout(new BorderLayout());
* c.add(BorderLayout.CENTER, new JScrollPane(new ZGraphique()));
* </pre>
*/
public class ZGraphique extends JComponent implements MouseListener,
MouseMotionListener, Observer
{
/** Vecteur de <code>ZElement</code> */
private List<ZElement> zelements = new ArrayList<ZElement>() ;
/** Vecteur de <code>ZLien</code> */
private List<ZLien> zliens = new ArrayList<ZLien>() ;
/**
* Cette variable sert pour le glisser-déposer. Lorsque l'on clic sur un
* <code>ZElement</code> la variable est liée à ce <code>ZElement</code>
* jusqu'à ce qu'on relache la souris
*/
private ZElement elementPress = null;
/**
* Cette variable est liée vers un <code>ZElement</code> sur lequel on a
* cliqué jusque l'on clique dans le vide ou sur un autre élément.
*/
protected ZElement elementClic = null;
/**
* Cet {@link Set ensemble} contient les {@link ZElement}s de la sélection
* courante.
*/
protected Set<ZElement> selectionCourante = new LinkedHashSet<ZElement>();
protected Set<ZElement> selectionTemporaire = new LinkedHashSet<ZElement>();
/**
* Cette variable est liée vers un <code>ZLien</code> sur lequel on a
* cliqué jusque l'on clique dans le vide ou sur un autre élément.
*/
protected ZLien lienClic = null;
protected Point departCadreSelection = null;
protected Point arriveeCadreSelection = null;
/**
* Cette variable est utilisée lors de la création d'un lien.
*/
private ZLien lienTemp;
/** Utilisé pour le déplacement des éléments. Contient la position relative par rapport à la souris. */
private Map<ZElement, Point> positionsRelatives = new HashMap<ZElement, Point>();
/** Possibilité d'intéragir sur le composant grâce à la souris. */
private boolean enabled = true;
/** Enclenche le mode 'création de lien' */
private boolean creationLien = false;
/** Indique si en mode 'création de lien', l'utilisateur a cliqué */
private boolean creationLienClic = false;
/** Gestion des événement */
private ActionListener actionListener;
/** Pour la position du lien */
private int x1, y1, x2, y2;
/**
* Définie un <code>ZGraphique</code> avec les paramètres par défaut.
*/
public ZGraphique()
{
setEnabled(false);
addMouseListener(this);
setSize(getPreferredSize());
setBackground(Color.white);
}
/**
* Active ou désactive l'intéraction de l'utilisateur avec le graphique.
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* Retourne la variable enabled.
*/
public boolean getEnabled()
{
return enabled;
}
/**
* Retourne le nombre de <code>ZElement</code>.
*/
public int sizeElements()
{
return zelements.size();
}
/**
* Retourne le nombre de <code>ZElement</code> actuellement sélectionnés.
*/
public int sizeSelection()
{
return selectionCourante.size();
}
/**
* Retourne le nombre de liens.
*/
public int sizeLien()
{
return zliens.size();
}
/**
* Retourne une Enumeration des <code>ZElement</code>.
*/
public Iterator<ZElement> enumElements()
{
return zelements.iterator();
}
/**
* Retourne une énumération des <code>ZLien</code>
*/
public Iterator<ZLien> enumLiens()
{
return zliens.iterator();
}
/**
* Ajoute un <code>ZElement</code> dans le composant.
*/
public void addElement(ZElement element)
{
zelements.add(element);
element.majObserver(this);
repaint();
}
/**
* Ajoute plusieurs <code>ZElement</code> dans le composant.
*/
public void addElements(List<ZElement> elements)
{
Iterator<ZElement> e = elements.iterator();
while (e.hasNext())
addElement((ZElement) e.next());
}
/**
* Supprime un <code>ZElement</code> du composant.
*/
public void removeElement(ZElement element)
{
enleverFocus();
Iterator<ZLien> e = zliens.iterator();
List<ZLien> supp = new ArrayList<ZLien>();
while (e.hasNext()) {
ZLien l = (ZLien) (e.next());
if (l.getElement(Constantes.MCDENTITE1).equals(element)
|| l.getElement(Constantes.MCDENTITE2).equals(element))
supp.add(l);
}
removeLiens(supp);
zelements.remove(element);
repaint();
}
/**
* Ajoute un <code>ZLien</code> dans le composant.
*/
public void addLien(ZLien lien) {
if (lien.getElement(Constantes.MCDENTITE1) != null && lien.getElement(Constantes.MCDENTITE2) != null
&& lien.getElement(Constantes.MCDENTITE1) != lien.getElement(Constantes.MCDENTITE2)
&& !zliens.contains(lien))
zliens.add(lien);
repaint();
}
/**
* Ajoute plusieurs <code>ZLien</code> dans le composant.
*/
public void addLiens(List<ZLien> liens)
{
Iterator<ZLien> e = liens.iterator();
while (e.hasNext())
addLien((ZLien) e.next());
}
/**
* Supprime un <code>ZLien</code> du composant.
*/
public void removeLien(ZLien lien)
{
enleverFocus();
zliens.remove(lien);
repaint();
}
/**
* Supprime plusieurs <code>ZLien</code> du composant.
*/
public void removeLiens(List<ZLien> supp)
{
for (Iterator<ZLien> e = supp.iterator(); e.hasNext();)
removeLien((ZLien) e.next());
}
/**
* Retourne un <code>ZLien</code> du composant.
*
* @param i
* index du lien à retourner
*/
public ZLien getLien(int i)
{
return ((ZLien) (zliens.get(i)));
}
/**
* Retourne le lien qui a le focus.
*/
public ZLien getLienFocus()
{
return lienClic;
}
/**
* Retourne une Enumeration des <code>ZLien</code>.
*/
public Iterator<ZLien> elementsZLiens()
{
return zliens.iterator();
}
/**
* Retourne un <code>ZElement</code> du composant.
*
* @param i
* index du composant à retourner
*/
public ZElement getElement(int i)
{
return ((ZElement) (zelements.get(i)));
}
/**
* Retourne le composant qui a le focus.
*/
public ZElement getElementFocus()
{
return elementClic;
}
/**
* Retourne une Enumeration des <code>ZElement</code>.
*/
public Iterator<ZElement> elementsZElements()
{
return zelements.iterator();
}
/**
* Retourne le premier {@link ZElement} se trouvant à la position x, y.
*/
public ZElement chercheElement(int x, int y)
{
for (int i = zelements.size() - 1; i >= 0; i--)
if (getElement(i).isSelected(x, y))
return getElement(i);
return null;
}
/**
* Retourne le premier {@link ZLien} se trouvant à la position x, y.
*/
public ZLien chercheLien(int x, int y)
{
for (int i = 0; i < zliens.size(); i++)
if (getLien(i).isSelected(x, y))
return getLien(i);
return null;
}
/**
* Retourne le premier composant se trouvant à la position x, y.
*/
public Object getObjectFromLocation(int x, int y)
{
ZElement elem = chercheElement(x, y);
if (elem != null)
return elem;
ZLien lien = chercheLien(x, y);
if (lien != null)
return lien;
return null;
}
/**
* Enlève le focus de tous les elements.
*/
public void enleverFocus()
{
elementClic = null;
selectionCourante.clear();
selectionTemporaire.clear();
lienClic = null;
}
/**
* Enclenche le mode de "création de liens". Il est alors possible de liée
* deux elements dans le graphe.
*/
public void creerLien(ZLien lien)
{
enleverFocus();
lienTemp = lien;
lienTemp.setElement(null, Constantes.MCDENTITE1);
lienTemp.setElement(null, Constantes.MCDENTITE2);
if (zliens.contains(lien))
removeLien(lien);
creationLien = true;
creationLienClic = false;
}
/**
* Désenclenche le mode "création de liens".
*/
public void annulerCreerLien()
{
enleverFocus();
lienTemp = null;
creationLien = false;
creationLienClic = false;
}
/**
* Cette méthode indique si l'on peut créer un lien entre deux éléments.
*/
public boolean peutCreerLien(ZElement elem1, ZElement elem2)
{
ZLien lien;
Iterator<ZLien> e = zliens.iterator();
while (e.hasNext()) {
lien = (ZLien) e.next();
if (lien.getElement(Constantes.MCDENTITE1) == elem1
&& lien.getElement(Constantes.MCDENTITE2) == elem2) {
return false;
}
}
return true;
}
public String infoZLiens()
{
String s = "{";
for (Iterator<ZLien> e = elementsZLiens(); e.hasNext();) {
ZLien lien = (ZLien) e.next();
s += "\n" + lien;
}
return s + "\n}";
}
public void clearAll()
{
enleverFocus();
zelements = new ArrayList<ZElement>();
zliens = new ArrayList<ZLien>();
repaint();
}
/**
* Dessine le composant.
*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
Iterator<ZElement> e = zelements.iterator();
while (e.hasNext())
((ZElement) (e.next())).paint(g);
Iterator<ZLien> e1 = zliens.iterator();
while (e1.hasNext())
((ZLien) (e1.next())).paint(g);
for (ZElement elem : selectionCourante)
elem.paintFocus(g);
for (ZElement elem : selectionTemporaire)
elem.paintFocus(g);
if (lienClic != null)
lienClic.paintFocus(g);
if (departCadreSelection != null) {
int x1 = (int)departCadreSelection.getX();
int y1 = (int)departCadreSelection.getY();
int x2 = (int)arriveeCadreSelection.getX();
int y2 = (int)arriveeCadreSelection.getY();
int w = x2 - x1;
int h = y2 - y1;
if (x2 < x1) {
x1 = x2;
w = -w;
}
if (y2 < y1) {
y1 = y2;
h = -h;
}
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.green);
g2d.draw(new Rectangle2D.Double(x1, y1, w, h));
}
if (creationLien && creationLienClic) {
Graphics2D g2d = (Graphics2D) g;
Rectangle2D r;
g2d.setColor(Color.black);
Line2D l = new Line2D.Double(x1, y1, x2, y2);
g2d.draw(l);
r = new Rectangle2D.Double(x1 - 1, y1 - 1, 4, 4);
g2d.fill(r);
r = new Rectangle2D.Double(x2 - 2, y2 - 1, 4, 4);
g2d.fill(r);
}
}
public Dimension getPreferredSize()
{
int x = 0, y = 0;
for (int i = 0; i < zelements.size(); i++) {
x = getElement(i).getX() + getElement(i).getWidth() > x ? getElement(
i).getX()
+ getElement(i).getWidth()
: x;
y = getElement(i).getY() + getElement(i).getHeight() > y ? getElement(
i).getY()
+ getElement(i).getHeight()
: y;
}
return new Dimension(x + 30, y + 30);
}
public void update(Observable o, Object arg)
{
this.repaint();
}
public void addActionListener(ActionListener listener)
{
actionListener = AWTEventMulticaster.add(actionListener, listener);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
public void removeActionListener(ActionListener listener)
{
actionListener = AWTEventMulticaster.remove(actionListener, listener);
}
public void mouseClicked(MouseEvent e)
{
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent e)
{
addMouseMotionListener(this);
if (creationLien && enabled) {
creationLienClic = true;
int x = e.getX();
int y = e.getY();
x1 = e.getX();
y1 = e.getY();
x2 = e.getX();
y2 = e.getY();
ZElement departChoisi = chercheElement(x, y);
if (departChoisi != null)
lienTemp.setElement(departChoisi, Constantes.MCDENTITE1);
repaint();
} else if (enabled) {
elementPress = null;
elementClic = null;
lienClic = null;
int x = e.getX();
int y = e.getY();
ZElement nouvelleSelection = chercheElement(x, y);
if (nouvelleSelection != null) {
elementPress = nouvelleSelection;
elementClic = nouvelleSelection;
if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == 0
&& !selectionCourante.contains(nouvelleSelection))
selectionCourante.clear();
selectionCourante.add(nouvelleSelection);
positionsRelatives.clear();
for (ZElement elem : selectionCourante)
positionsRelatives.put(elem, new Point(x - elem.getX(), y - elem.getY()));
}
else {
ZLien nouvelleSelectionLien = chercheLien(x, y);
if (nouvelleSelectionLien != null) {
lienClic = nouvelleSelectionLien;
selectionCourante.clear();
} else {
departCadreSelection = new Point(x, y);
arriveeCadreSelection = new Point(x, y);
if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == 0)
selectionCourante.clear();
}
}
}
}
public void mouseReleased(MouseEvent e)
{
removeMouseMotionListener(this);
if (creationLien && enabled) {
creationLien = false;
ZElement cibleLien = chercheElement(e.getX(), e.getY());
if (cibleLien != null) {
lienTemp.setElement(cibleLien, Constantes.MCDENTITE2);
x2 = e.getX();
y2 = e.getY();
}
if (peutCreerLien(lienTemp.getElement(Constantes.MCDENTITE1), lienTemp
.getElement(Constantes.MCDENTITE2))){
addLien(lienTemp);
}
else
lienTemp.clearElement();
repaint();
} else if (enabled) {
elementPress = null;
if (arriveeCadreSelection != null) {
selectionCourante.addAll(selectionTemporaire);
selectionTemporaire.clear();
departCadreSelection = null;
arriveeCadreSelection = null;
}
repaint();
}
if (actionListener != null)
actionListener.actionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, ""));
/* Correction Bug Graphique */
if (getPreferredSize().height > getSize().height
|| getPreferredSize().width > getSize().width
/*
* || getPreferredSize().height < getSize().height ||
* getPreferredSize().width < getSize().width
*/)
this.setSize(getPreferredSize());
}
public void mouseDragged(MouseEvent e)
{
Point point = new Point();
if (creationLien && enabled) {
x2 = e.getX();
y2 = e.getY();
repaint();
} else if (enabled) {
if (elementPress != null) {
for (Map.Entry<ZElement, Point> entry : positionsRelatives.entrySet()) {
Point posRel = entry.getValue();
int xCentrage = (int) posRel.getX();
int yCentrage = (int) posRel.getY();
point.x = e.getX() - xCentrage < 0 ? 0 : e.getX() - xCentrage;
point.y = e.getY() - yCentrage < 0 ? 0 : e.getY() - yCentrage;
ZElement elem = entry.getKey();
elem.setPosition(point);
}
repaint();
}
else if (arriveeCadreSelection != null) {
arriveeCadreSelection.x = e.getX();
arriveeCadreSelection.y = e.getY();
selectionTemporaire.clear();
for (ZElement elem : zelements)
if (elem.isInside(departCadreSelection.x, departCadreSelection.y, arriveeCadreSelection.x, arriveeCadreSelection.y))
selectionTemporaire.add(elem);
repaint();
}
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
}
}