UmlGui.java

package algorithmen.umleditor;

import java.util.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
 * Baut die graphische Oberfläche auf.
 * Manager für alle Elemente eines UML-Diagramms.
 * Ist zuständig für alle globalen Operationen wie laden und speichern.
 * Enthält eine Liste aller Views
 */
public class UmlGui extends JApplet implements Serializable {
  
  protected java.util.List views;
  protected JFileChooser fc;
  
  protected int state;   // action that is just being performed
  
  // types of actions
  protected static final int POINTING = 0;
  protected static final int EDITING = 1;
  protected static final int DELETING = 2;
  protected static final int CHOOSING_LEFT_H = 3;
  protected static final int CHOOSING_RIGHT_H = 4;
  protected static final int CHOOSING_LEFT_V = 5;
  protected static final int CHOOSING_RIGHT_V = 6;
  protected static final int MOVING = 7;
  
  protected View leftObject;  // object found with the first click
  
  /** Creates a new instance of UMLGui */
  public UmlGui() {
    init();
  }
  
  public void init() {
    Container cp = getContentPane();
    
    // Erzeuge den Toolbar
    JToolBar toolBar = new JToolBar();
    addButtons(toolBar);
    
    // Erzeuge die Zeichenfläche
    JPanel drawArea = new DisplayPanel();
    DisplayMouseListener dml = new DisplayMouseListener();
    drawArea.addMouseListener(dml);
    drawArea.addMouseMotionListener(dml);
    JScrollPane scrollPane = new JScrollPane(drawArea);
    
    cp.add(toolBar, BorderLayout.NORTH);
    cp.add(scrollPane, BorderLayout.CENTER);
    
    try {  // keine Dateioperation möglich, wenn UmlGui als Applet läuft
      fc = new JFileChooser();
    } catch(Exception e) {
      // ok, tue nichts
    }
    
    views = new ArrayList();
    state = POINTING;
  }
  
  /**
   * Fügt die Buttons zum Toolbar hinzu
   */
  protected void addButtons(JToolBar toolbar) {
    JButton button;
    
    // Button Laden
    button = new JButton(getIcon("load"));
    button.setToolTipText("Laden");
    button.addActionListener(new LoadListener());
    toolbar.add(button);
    
    // Button Speichern
    button = new JButton(getIcon("save"));
    button.setToolTipText("Speichern");
    button.addActionListener(new SaveListener());
    toolbar.add(button);
    toolbar.addSeparator();
    
    // Button Neue Klasse
    button = new JButton(getIcon("class"));
    button.setToolTipText("Neue Klasse");
    button.addActionListener(new AddClassListener());
    toolbar.add(button);
    
    // Button Neues Interface
    button = new JButton(getIcon("interface"));
    button.setToolTipText("Neues Interface");
    button.addActionListener(new AddInterfaceListener());
    toolbar.add(button);
    
    // Button Neues Objekt
    button = new JButton(getIcon("object"));
    button.setToolTipText("Neues Objekt");
    button.addActionListener(new AddObjectListener());
    toolbar.add(button);
    toolbar.addSeparator();
    
    // Button Neuer Pfeil vertikal
    button = new JButton(getIcon("up"));
    button.setToolTipText("Neuer vertikaler Pfeil");
    button.addActionListener(new AddUpArrowListener());
    toolbar.add(button);
    
    // Button Neuer Pfeil horizontal
    button = new JButton(getIcon("right"));
    button.setToolTipText("Neuer horizontaler Pfeil");
    button.addActionListener(new AddRightArrowListener());
    toolbar.add(button);
    toolbar.addSeparator();
    
    // Button Edit
    button = new JButton(getIcon("edit"));
    button.setToolTipText("Editieren");
    button.addActionListener(new EditListener());
    toolbar.add(button);
    
    // Button Delete
    button = new JButton(getIcon("delete"));
    button.setToolTipText("Löschen");
    button.addActionListener(new DeleteListener());
    toolbar.add(button);
  }
  
  /**
   * Fügt den View eines UML-Objekts in die Liste ein.
   */
  public void addUmlElement(View o) {
    views.add(o);
    repaint();
  }
  
  /**
   * löscht alle Views aus der Liste.
   */
  public void clear() {
    views.clear();
  }
  
  /**
   * Speichert die Liste aller Views in der Datei file.
   */
  public void save() {
    if (fc.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) {
      return;
    }
    
    File file = fc.getSelectedFile();
    try {
      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
      out.writeObject(views);
      out.close();    // flushes output
    } catch (Exception e) {
      System.err.println("Fehler beim Speichern in " + file.getName());
      e.printStackTrace();
    }
  }
  
  /**
   * Lädt die Liste aller Views aus der Datei file.
   */
  public void load() {
    if (fc.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
      return;
    }
    
    File file = fc.getSelectedFile();
    try {
      ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
      views = (java.util.List) in.readObject();
    } catch (Exception e) {
      System.err.println("Fehler beim Laden von " + file.getName());
      e.printStackTrace();
    }
    
    repaint();
  }
  
  public static void main(String[] args) {
    // erzeuge ein neues Fenster, das sich richtig beendet
    JFrame meinFenster = new JFrame("UML Editor");
    meinFenster.setDefaultCloseOperation(meinFenster.EXIT_ON_CLOSE);
    
    // packe ein ZeichenFenster hinein
    Container p = meinFenster.getContentPane();
    p.add(new UmlGui());
    
    // zeige das Fenster in bestimmter Größe an
    meinFenster.setSize(640, 480);
    meinFenster.setVisible(true);
    
  }
  
  // interne Methoden
  
  /*
   * Erzeugt ein ImageIcon aus der Datei name im images-Verzeichnis.
   * Hängt Endung .gif an name an.
   */
  protected ImageIcon getIcon(String name) {
    // Problem:
    // Programm läuft als Applet und Applikation ->
    //   als Applet kann der Pfad nicht direkt verwendet werden,
    //   als Applikation kann getCodebase von Applet nicht benutzt werden.
    //  Daher verwende ich den ClassLoader explizit
    //     -> Basis-URL bezieht sich auf Spitze der Paket-Hierarchie
    ClassLoader myCL = getClass().getClassLoader();
    String urlString = "algorithmen/umleditor/images/" + name + ".gif";
    URL myURL = myCL.getResource(urlString);
    if (myURL != null) {
      return new ImageIcon(myURL);
    } else {
      return null;
    }
  }
  
  /**
   * Bestimmt das umgebende Frame.
   * Vor allem für das Applet wichtig.
   */
  protected Frame getFrame() {
    Container c = getParent();
    while (c != null && !(c instanceof Frame)) {
      c = c.getParent();
    }
    return (Frame) c;
  }
  
  class DisplayPanel extends JPanel {
    public void paintComponent(Graphics g) {
      setBackground(Color.WHITE);
      super.paintComponent(g);
      
      // laufe durch die Objektliste
      for (Iterator i = views.iterator(); i.hasNext(); ) {
        View v = (View) i.next();
        v.draw(g);
      }
    }
  }
  
  class LoadListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (fc != null) {  // no load when running as applet
        load();
      }
    }
  }
  
  class SaveListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (fc != null) {   // no save when running as applet
        save();
        
      }}
  }
  
  class AddClassListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      addUmlElement(new ClassView());
    }
  }
  
  class AddInterfaceListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      addUmlElement(new InterfaceView());
    }
  }
  
  class AddObjectListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      addUmlElement(new ObjectView());
    }
  }
  
  class AddRightArrowListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      state = CHOOSING_LEFT_H;
      setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
    }
  }
  
  class AddUpArrowListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      state = CHOOSING_LEFT_V;
      setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
    }
  }
  
  class EditListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      state = EDITING;
      setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
    }
  }
  
  class DeleteListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      state = DELETING;
      setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
    }
  }
  
  class DisplayMouseListener extends MouseAdapter
  implements MouseMotionListener {
    
    // aktives Objekt einer Mausoperation
    protected View hitObjectView = null;
    // Koordinaten des letzten mousePressed
    protected Point lastPoint;
    
    public void mousePressed(MouseEvent e){
      // prüfe, welches Objekt getroffen wurde
      hitObjectView = null;
      for (Iterator i = views.iterator(); i.hasNext(); ) {
        View v = (View) i.next();
        if (v.contains(e.getPoint())) {
          hitObjectView = v;
          break;
        }
      }
      
      // nichts getroffen -> zurück in Grundzustand
      if (hitObjectView == null) {
        state = POINTING;
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        return;
      }
      
      // Aktion je nach Zustand durchführen
      Arrow a;
      switch (state) {
        case DELETING:
          views.remove(hitObjectView);
          setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          state = POINTING;
          repaint();
          break;
        case EDITING:
          hitObjectView.createEditor(hitObjectView.getModel(), getFrame());
          setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          state = POINTING;
          repaint();
          break;
        case POINTING:
          lastPoint = e.getPoint();
          state = MOVING;
          break;
        case CHOOSING_LEFT_H:
          leftObject = hitObjectView;
          state = CHOOSING_RIGHT_H;
          break;
        case CHOOSING_LEFT_V:
          leftObject = hitObjectView;
          state = CHOOSING_RIGHT_V;
          break;
        case CHOOSING_RIGHT_H:
          a = new Arrow(leftObject.getModel(), hitObjectView.getModel(),
          ArrowType.REFERENCE);
          addUmlElement(new ArrowView(a, leftObject, hitObjectView));
          setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          state = POINTING;
          repaint();
          break;
        case CHOOSING_RIGHT_V:
          a = new Arrow(leftObject.getModel(), hitObjectView.getModel(),
          ArrowType.EXTENSION);
          addUmlElement(new ArrowView(a, leftObject, hitObjectView));
          setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          state = POINTING;
          repaint();
          break;
      }
    }
    
    public void mouseDragged(MouseEvent e) {
      if (hitObjectView != null && state == MOVING) {
        hitObjectView.translate(e.getX() - lastPoint.x, e.getY() - lastPoint.y);
        lastPoint = e.getPoint();
        repaint();
      }
    }
    
    public void mouseReleased(MouseEvent e){
      if (hitObjectView != null && state == MOVING) {
        hitObjectView.translate(e.getX() - lastPoint.x, e.getY() - lastPoint.y);
        hitObjectView = null;
        state = POINTING;
        repaint();
      }
    }
    
    public void mouseMoved(MouseEvent e) {  // tue nichts
    }
    
  }
}