import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.List; /** * Cette classe permet d'afficher l'ensemble des éléments du projet sous la forme d'un diagramme UML. * @author V.BOULANGER */ public class UmlDiagram extends JPanel implements MouseListener, MouseMotionListener { private Project _project; private AppThinkerToolbar _toolbar; private List _classes; private List _links; private Object _selected; private String cornerSelection = null; private int _shiftX; private int _shiftY; /** * Constructeur - Crée un nouveau diagramme UML à partir d'un projet. * @param p Le projet associé. */ public UmlDiagram(Project p){ _project = p; this.addMouseListener(this); this.addMouseMotionListener(this); this.setPreferredSize(new Dimension(3000,3000)); this._toolbar = new AppThinkerToolbar(this); } /** * Dessine les éléments du projet sous la forme d'un diagramme UML. * @param g L'objet graphique */ @Override public void paintComponent(Graphics g){ Font font1 = new Font("Arial", Font.PLAIN, 14); FontMetrics metrics1 = this.getFontMetrics(font1); Font font2 = new Font("Arial", Font.PLAIN, 10); FontMetrics metrics2 = this.getFontMetrics(font2); g.setColor(new Color(127, 158, 178)); g.drawString("UML Diagram", 10, 20); for(Class a : _classes){ g.setFont(font1); int posX = a.getPosX() - (a.getSizeX()/2); int posY = a.getPosY() - (a.getSizeY()/2); //Dessin du rectangle g.setColor(new Color(127, 158, 178)); g.fillRect(posX, posY, a.getSizeX(), a.getSizeY()); g.setColor(Color.BLACK); //Dessin du nom de la classe g.setColor(new Color(39, 76, 94)); int posCounter = posY + font1.getSize(); g.drawString(a.getName(), posX + a.getSizeX()/2 - metrics1.stringWidth(a.getName())/2, posCounter); posCounter += 5; g.setColor(new Color(218, 233, 244)); //Ligne de séparation g.drawLine(posX, posY + font1.getSize() + 5, posX + a.getSizeX()-1, posY + font1.getSize() + 5); g.setFont(font2); posCounter += font2.getSize(); g.drawString("attributes", posX + a.getSizeX()/2 - metrics2.stringWidth("attributes")/2, posCounter); //Affichage des attributs g.setColor(new Color(39, 76, 94)); for(Attribute b : a.getAttributes()){ posCounter += font2.getSize(); g.drawString(b.getAccess() + " " + b.getName() + " : " + b.getType(), posX, posCounter); } posCounter += 5; g.setColor(new Color(218, 233, 244)); //Ligne de séparation g.drawLine(posX, posCounter, posX + a.getSizeX()-1, posCounter); posCounter += font2.getSize(); g.drawString("methods", posX + a.getSizeX()/2 - metrics2.stringWidth("methods")/2, posCounter); //Dessin des méthodes g.setColor(new Color(39, 76, 94)); for(Method m : a.getMethods()){ posCounter += font2.getSize(); String chain = m.getAccess() + " " + m.getName() + "("; ArrayList listArguments = new ArrayList(); for(Argument ar : m.getArguments()){ listArguments.add(ar.getName() + " : " + ar.getType()); } //chain = chain.substring(0, chain.length()-2); chain += String.join(", ", listArguments) + ") : " + m.getType(); g.drawString(chain, posX, posCounter); } //Si la classe est sélectionnée if((Class)_selected == a){ g.setColor(new Color(39, 76, 94)); AppThinker.getWindow().getStatusbar().setSizeLabel(a.getSizeX(), a.getSizeY()); //Top Left g.fillOval(posX-4, posY-4, 8, 8); //Top g.fillRect(posX + (a.getSizeX()/2)-4, posY-4, 8, 8); //Top Right g.fillOval(posX + a.getSizeX()-4, posY-4, 8, 8); //Right g.fillRect(posX + a.getSizeX()-4, posY + (a.getSizeY()/2)-4, 8, 8); //Bottom Right g.fillOval(posX + a.getSizeX()-4, posY + a.getSizeY()-4, 8, 8); //Bottom g.fillRect(posX + (a.getSizeX()/2)-4, posY + a.getSizeY()-4,8, 8); //Bottom Left g.fillOval(posX - 4, posY + a.getSizeY()-4, 8, 8); //Left g.fillRect(posX -4, posY + (a.getSizeY()/2)-4, 8, 8); //Total rectangle g.drawRect(posX, posY, a.getSizeX(), a.getSizeY()); } else AppThinker.getWindow().getStatusbar().setSizeLabel(0, 0); } AppThinker.getWindow().repaint(); } /** * Récupère la toolbar du diagramme. * @return La toolbar du diagramme. */ public AppThinkerToolbar getToolbar(){ return this._toolbar; } /** * Mets à jour graphiquement le diagramme UML. */ public void displayDiagram(){ _classes = _project.getClasses(); _links = _project.getLinks(); this.repaint(); } /** * Récupère l'élément sélectionné dans la grille. * @return L'élément sélectionné dan sla grille. */ public Object getSelected(){ return this._selected; } /** * Récupération de l'objet cliqué * @param getX Les coordonnées de la souris sur l'axe X. * @param getY Les coordonnées de la souris sur l'axe Y. */ public void select(int getX, int getY){ //On cherche l'objet sélectionné boolean classSelected = false; boolean linkSelected = false; for(Class a : _classes){ int posX = a.getPosX()-(a.getSizeX()/2); int posY = a.getPosY()-(a.getSizeY()/2); //Si la souris est dans la classe if (getX >= posX && getX <= (posX + a.getSizeX())) { if (getY >= posY && getY <= (posY + a.getSizeY())) { this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); //On sélectionne la classe classSelected = true; _selected = a; break; } } } if(classSelected == false && linkSelected == false) _selected = null; //Si la classe est sélectionnée, on enregistre le décalage entre la souris et le centre de la classe if(_selected instanceof Class){ Class a = (Class) _selected; _shiftX = getX - a.getPosX(); _shiftY = getY - a.getPosY(); } this.repaint(); } /** * Modifie les propriétés de la classe. * @param a La classe a modifier. */ public void editClass(Class a){ ClassPropertiesWindow prop = new ClassPropertiesWindow(this, a); } public void removeClass(Class a){ AppThinker.getProject().getClasses().remove(a); } //Actions de la souris sur le diagramme UML /** * Action du clic de la souris sur le diagramme. * @param e L'événement souris. */ @Override public void mouseClicked(MouseEvent e) { //Si il s'agit d'un double-clic et que l'outil sélection est sélectionné, on ouvre la fenêtre d'édition de la classe if(e.getClickCount() == 2 && _toolbar.getCurrentTool() == AppThinkerToolbar.SELECT_TOOL){ if(_selected instanceof Class){ this.editClass((Class) _selected); } } } /** * Bouton de la souris pressé sur la grille. On récupère l'outil sélectionné pour parvenir à l'action. * @param e L'événement souris. */ @Override public void mousePressed(MouseEvent e) { int tool = _toolbar.getCurrentTool(); switch(tool){ //On essaie de sélectionner un élément case AppThinkerToolbar.EDIT_TOOL: System.out.println("On édite un élément."); this.select(e.getX(), e.getY()); if(_selected instanceof Class){ this.editClass((Class) _selected); } break; case AppThinkerToolbar.DELETE_TOOL: System.out.println("On supprime un élément."); this.select(e.getX(), e.getY()); if(_selected instanceof Class){ removeClass((Class)_selected); } this.displayDiagram(); break; case AppThinkerToolbar.COPY_TOOL: System.out.println("On copie un élément."); break; case AppThinkerToolbar.PASTE_TOOL: System.out.println("On colle un élément."); break; //On essaie d'ajouter une classe case AppThinkerToolbar.CLASS_TOOL: AppThinker.getProject().addClass(new Class(e.getX(), e.getY(), Class.RECTANGLE)); break; case AppThinkerToolbar.ASSOCIATION_TOOL: System.out.println("On ajoute une association"); break; case AppThinkerToolbar.LINK_TOOL: System.out.println("On ajoute un lien"); break; default: this.select(e.getX(), e.getY()); break; } this.displayDiagram(); } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } /** * Déplacer un élément en cliquant et déplaçant la souris. * @param e Evénement souris */ @Override public void mouseDragged(MouseEvent e) { //Mise à jour des coordonnées de la souris dans la statusbar int posX = e.getX(); int posY = e.getY(); AppThinker.getWindow().getStatusbar().setPosLabel(posX, posY); if(_selected instanceof Class){ Class a = (Class)_selected; int shiftX = 0; int shiftY = 0; //Si un coin est sélectionné, on redimensionne if(cornerSelection != null){ switch(cornerSelection){ case "N": a.resizeUp(posY); break; case "NE": a.resizeUp(posY); a.resizeRight(posX); case "E": a.resizeRight(posX); break; case "SE": a.resizeDown(posY); a.resizeRight(posX); //Redimensionnement bas case "S": a.resizeDown(posY); break; case "SW": a.resizeDown(posY); a.resizeLeft(posX); break; case "W": a.resizeLeft(posX); break; case "NW": a.resizeUp(posY); a.resizeLeft(posX); break; } } //Sinon on déplace else{ //On repositionne la classe en prenant en compte le décalage mesuré au clic de la souris a.setPosX(posX - _shiftX); a.setPosY(posY - _shiftY); this.setCursor(new Cursor(Cursor.MOVE_CURSOR)); } } this.repaint(); } /** * La souris bouge dans la grille. * @param e L'événement souris. */ @Override public void mouseMoved(MouseEvent e) { int getX = e.getX(); int getY = e.getY(); int sens = 5; //Mise à jour des coordonnées de la souris dans la statusbar AppThinker.getWindow().getStatusbar().setPosLabel(e.getX(), e.getY()); //Si une classe est sélectionnée if (_selected instanceof Class) { Class a = (Class) _selected; int posX = a.getPosX() - (a.getSizeX() / 2); int posY = a.getPosY() - (a.getSizeY() / 2); //Si la souris est à proximité d'un lien, on affiche le curseur de redimensionnement if (getX >= posX - sens && getX <= posX + sens && getY >= posY - sens && getY <= posY + sens) { cornerSelection = "NW"; this.setCursor(new Cursor(Cursor.NW_RESIZE_CURSOR)); } else if (getX >= posX + a.getSizeX() / 2 - sens && getX <= posX + a.getSizeX() / 2 + sens && getY >= posY - sens && getY <= posY + sens) { cornerSelection = "N"; this.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR)); } else if (getX >= posX + a.getSizeX() - sens && getX <= posX + a.getSizeX() + sens && getY >= posY - sens && getY <= posY + sens) { cornerSelection = "NE"; this.setCursor(new Cursor(Cursor.NE_RESIZE_CURSOR)); } else if (getX >= posX + a.getSizeX() - sens && getX <= posX + a.getSizeX() + sens && getY >= posY + a.getSizeY() / 2 - sens && getY <= posY + a.getSizeY() / 2 + sens) { cornerSelection = "E"; this.setCursor(new Cursor(Cursor.E_RESIZE_CURSOR)); } else if (getX >= posX + a.getSizeX() - sens && getX <= posX + a.getSizeX() + sens && getY >= posY + a.getSizeY() - sens && getY <= posY + a.getSizeY() + sens) { cornerSelection = "SE"; this.setCursor(new Cursor(Cursor.SE_RESIZE_CURSOR)); } else if (getX >= posX + a.getSizeX() / 2 - sens && getX <= posX + a.getSizeX() / 2 + sens && getY >= posY + a.getSizeY() - sens && getY <= posY + a.getSizeY() + sens) { cornerSelection = "S"; this.setCursor(new Cursor(Cursor.S_RESIZE_CURSOR)); } else if (getX >= posX - sens && getX <= posX + sens && getY >= posY + a.getSizeY() - sens && getY <= posY + a.getSizeY() + sens) { cornerSelection = "SW"; this.setCursor(new Cursor(Cursor.SW_RESIZE_CURSOR)); } else if (getX >= posX - sens && getX <= posX + sens && getY >= posY + a.getSizeY() / 2 - sens && getY <= posY + a.getSizeY() / 2 + sens) { cornerSelection = "W"; this.setCursor(new Cursor(Cursor.W_RESIZE_CURSOR)); } else { cornerSelection = null; this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } } }