# Grafiken mit Listener verbinden?



## KS (8. Dez 2005)

Hallo

Ich habe mit Rectangle2D ein par Vierecke in eine Grafik gemalt. Nun möchte ich diese Vierecke kontrollieren, falls jemand mit der Maus darauf klickt. Komme hier bei Rectangle2D nicht all zu weit, da keine addMouseListener Methode zur Verfügung steht. Ich hab mal mit implementieren begonnen, aber ich frage mich, ob das wirklich das wahre ist..

Wie schauts aus? Kann mir da jemand helfen? Vielen Dank im Voraus

Gruss
Chris


----------



## André Uhres (8. Dez 2005)

Was soll's denn werden wenn's fertig ist ?

EDIT: Falls du die Rechtecke verschieben willst, könntest du mit Polygon#translate(..) arbeiten.


----------



## KS (8. Dez 2005)

Etwas ähnliches wie ein Klalender von thunderbird.. wo du rechtecke als termine siehst und diese mit doppelklick auch gleich bearbeiten kannst..


----------



## André Uhres (8. Dez 2005)

Den Thunderbird Kalender kenne ich nicht, aber so wie ich dein Problem einschätze
ist es wahrscheinlich besser wenn du eine JTable benutzen würdest.


----------



## KS (8. Dez 2005)

..mmh.. ich habe bereits eine tabelle, welche termine anzeigt. unten dran hab ich eine kalender grafik welche mir die termine als rechtecke anzeigt. was ich nun will ist wenn ich die rechtecke anklicke z.bsp. der entsprechende eintrag in der Tabelle selektiert wird.

ich dachte man könnte ein viereckobjekt mit der addMouseListener methoder erweitern.. so habe ich gedacht.. geht das denn nicht???


----------



## André Uhres (8. Dez 2005)

In dem Fall könntest du vielleicht anstatt der Rechtecke JComponents benutzen (z.B. JLabel, JButton oder JPanel).
Da kannst du problemlos MouseListeners dranhängen.

EDIT: aber wahrscheinlich ist es einfacher eine zweite JTable anzulegen wo du einen
MouseListener dranhängst. Bei mouseClicked selektierst du dann die gleiche Zelle in der ersten Tabelle.

```
jTable1.setCellSelectionEnabled(true);
        jTable2.setCellSelectionEnabled(true);
        jTable2.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent evt) {
                jTable2MouseClicked(evt);
            }
        });
        ...
    private void jTable2MouseClicked(MouseEvent evt) {
        jTable1.changeSelection(jTable2.getSelectedRow(), jTable2.getSelectedColumn(), false, false);
    }
```


----------



## KS (9. Dez 2005)

super idee mit den panels! das ist das was ich suchte!! das mit den 2 tabellen verstehe ich nicht ganz. 
um dir zu zeigen wie es etwa aussieht ein bild meiner applikation:







was du siehst: die blauen flächen sind momentan noch Objekte von Rectangle2D, 
welche ich jetzt durch JPanels ersetzten werde.. ich habe jedoch noch Probleme mit dem update, 
denn wenn ich die Rectangle2D Objekte durch JPanels ersetze, funktioniert das update des 
Jpanels nicht mehr korrekt.

Hier ist der Code meiner Klasse GUIPanelGraf (dies ist die ganze Grafik unterhalb der Tabelle):


```
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.Graphics2D;
import java.awt.Dimension;
import java.util.GregorianCalendar;

/**
 * 

Überschrift: Eventer</p>
 *
 * 

Beschreibung: Datebankapplikation für Eventmanagement</p>
 *
 * 

Copyright: Copyright (c) 2005</p>
 *
 * 

Organisation: KSF Engineering</p>
 *
 * @author Christian Schmitz
 * @version 0.2
 */
public class GUIPanelGraf extends JPanel {

	private GUIEventProgrammController controller;
	
	private GregorianCalendar calStart = new GregorianCalendar();

	private GregorianCalendar calEnd = new GregorianCalendar();

	Graphics2D g;
	
	//Eckpunkte der Grafik
	private int nOriginX = 0;
	private int nOriginY = 0;
	private int nSizeX = getSize().width;
	private int nSizeY = getSize().height;
	
	//Zeitlinie Startpunkt x
	private int nStartTimeLineX;
	
	
	private final int nFooterSizeY = 30;
	private final int nTimeFieldMinimalSizeY = 30;
	
	private EventProgramm[] aEventProgramm = null;
	private int[] aRowGroups = null;
	private double nTimeScreenFaktor;

	public GUIPanelGraf(Event objEvent) {
		this.calStart = objEvent.getEventDatumStart();
		this.calEnd = objEvent.getEventDatumEnde();
		this.aEventProgramm = objEvent.getEventProgramm();
		//hier habe ich eine methode dem objekt event angefügt, welches warsch. nur einmal gebraucht wird.. -> sinnvoll???
		this.aRowGroups = objEvent.getActsWithTermin();
		//Size muss später noch angepasst werden (100 = dynamisch auf minimum!!)
		if (aRowGroups == null){
			this.setPreferredSize(new Dimension(this.getSize().width,nFooterSizeY + nTimeFieldMinimalSizeY));	
		} else {
			this.setPreferredSize(new Dimension(this.getSize().width,nFooterSizeY + (nTimeFieldMinimalSizeY * aRowGroups.length)));
		}
	}
	
	public void initialize(GUIEventProgrammController controller) {
		this.controller = controller;
	}

	public void paint(Graphics ga) {

		g = (Graphics2D) ga;

		this.nOriginX = 0;
		this.nOriginY = 0;
		this.nSizeX = getSize().width;
		this.nSizeY = getSize().height;
		
		g.setColor(Color.blue);
		g.drawRoundRect(nOriginX, nOriginY, nSizeX - 1, nSizeY - 1, 16, 16);
		g.setColor(Color.black);
		FontMetrics fm = g.getFontMetrics(g.getFont());
		String s = "" + nSizeX + " x " + nSizeY + " Pixel";
		g.drawString(s, (nSizeX - fm.stringWidth(s)) / 2, (nSizeY - fm
				.getHeight())
				/ 2 + fm.getAscent());
		drawTimeLine();
		drawTimeFields();
		
	}

	private void drawTimeLine() {

		nStartTimeLineX = nOriginX + 10;
		int nStartY = nSizeY - nFooterSizeY;
		int nEndX = nSizeX - 10;
		int nEndY = nStartY;
		
		int nRasterHöhePrimär = nStartY - 10;
		int nRasterTiefePrimär = 5;
		int nRasterHöheSekundär = 3;
		int nRasterTiefeSekundär = 5;
		
		String sTimeLineTextStart = "Event Start";
		String sTimeLineTextEnd = "Event Ende";
		
		//Zeitlinie zeichnen
		Line2D lTimeLine = new Line2D.Double(nStartTimeLineX, nStartY, nEndX, nEndY);
		g.draw(lTimeLine);

		//Zeitlinienbeschriftung unterhalb
		g.setColor(Color.gray);
		FontMetrics fm = g.getFontMetrics(g.getFont());
		g.drawString(sTimeLineTextStart, nStartTimeLineX, nStartY + nRasterTiefePrimär + 2 + fm.getHeight());
		g.drawString(sTimeLineTextEnd, nEndX - fm.stringWidth(sTimeLineTextEnd), nStartY + nRasterTiefePrimär + 2	+ fm.getHeight());

		//Raster setzten
		int nTimeLineWidth = (int) (lTimeLine.getX2() - lTimeLine.getX1());
		long nTimeLineRange = calEnd.getTimeInMillis() - calStart.getTimeInMillis();
		nTimeScreenFaktor = (double)nTimeLineWidth / nTimeLineRange;
		GregorianCalendar calTemp = new GregorianCalendar(calStart.get(GregorianCalendar.YEAR),calStart.get(GregorianCalendar.MONTH),calStart.get(GregorianCalendar.DAY_OF_MONTH),0,0,0);
		//Raster zeichnen (Tagesgrenzen)
		while (calEnd.after(calTemp)) {
			calTemp.add(GregorianCalendar.DAY_OF_MONTH,1);
			int nTempX = (int) ((calTemp.getTimeInMillis() - calStart.getTimeInMillis()) * nTimeScreenFaktor) + nStartTimeLineX;
			g.draw(new Line2D.Double(nTempX, nStartY - nRasterHöhePrimär, nTempX, nEndY + nRasterTiefePrimär));
			//Datum hinzufügen falls platz
			if (nStartTimeLineX + fm.stringWidth(sTimeLineTextStart) < nTempX & nTempX + fm.stringWidth(calTemp.get(calTemp.DAY_OF_MONTH) + "." + (calTemp.get(calTemp.MONTH) + 1)) < nTimeLineWidth - fm.stringWidth(sTimeLineTextEnd)) {
				g.drawString(calTemp.get(calTemp.DAY_OF_MONTH) + "." + (calTemp.get(calTemp.MONTH) + 1), nTempX, nStartY + nRasterTiefePrimär + 2	+ fm.getHeight());
			}
		}
	}

	private void drawTimeFields() {
		if (aRowGroups == null)
			return;
		int nTimeFieldSizeY = (nSizeY - nFooterSizeY - 10) / (aRowGroups.length + 1);
		if (nTimeFieldSizeY < nTimeFieldMinimalSizeY) 
			nTimeFieldSizeY = nTimeFieldMinimalSizeY;
		for (int i = 0; i < aRowGroups.length; i++) {
			for (int j = 0; j < aEventProgramm.length; j++){
				if (aRowGroups[i] == aEventProgramm[j].getActID()) {
					System.out.println("zeichne: " + aEventProgramm[j].getEventProgrammText());
					int nTempX1 = (int) ((aEventProgramm[j].getEventProgrammDatumStart().getTimeInMillis() - calStart.getTimeInMillis()) * nTimeScreenFaktor) + nStartTimeLineX;
					int nTempX2 = (int) ((aEventProgramm[j].getEventProgrammDatumEnde().getTimeInMillis() - calStart.getTimeInMillis()) * nTimeScreenFaktor) + nStartTimeLineX;
					Rectangle2D r = new Rectangle2D.Double(nTempX1,nOriginY+20+(i*nTimeFieldSizeY),nTempX2-nTempX1,nTimeFieldSizeY-10);
					g.setPaint(Color.blue);
					g.fill(r);
					//JPanelTimeField p = new JPanelTimeField(nTempX1,nOriginY+20+(i*nTimeFieldSizeY),nTempX2-nTempX1,nTimeFieldSizeY-10);
					//this.add(p);
					//p.repaint();
					//System.out.println("x: " + nTempX1 + "\ny: " + (nOriginY+20+(i*nTimeFieldSizeY)) + "\nxw: " + (nTempX2-nTempX1) + "\nyw: " + nTimeFieldSizeY);
				}
			}
		}
	}
}

class JPanelTimeField extends JPanel {
	
	Graphics2D g;
	
	public JPanelTimeField(int nX, int nY, int nXWidth, int nYHeight) {
		this.setBounds(nX,nY,nXWidth,nYHeight);
	}
	
	public void paint(Graphics ga) {
		g = (Graphics2D) ga;
		
		Rectangle2D r = new Rectangle2D.Double(this.getX(),this.getY(),this.getSize().width,this.getSize().height);
		System.out.println("x: " + this.getX() + "\ny: " + this.getY() + "\nxw: " + this.getSize().width + "\nyw: " + this.getSize().height);
		g.setPaint(Color.WHITE);
		g.fill(r);
		g.setPaint(Color.GRAY);
		g.drawRect(this.getX(),this.getY(),this.getSize().width,this.getSize().height);
	}
	
}
```

in der methode drawTimeFields() werden die Einträge der Tabelle als Zeitfelder gezeichnet. 
im innersten Loop zeichne ich die Rectangle2D. gleich darunter ist der Code (noch auskommentiert) 
für die JPanels (JPanelTimeField(eigene Klasse)), welche als Ersatz der Rectangle2D stehen sollten. 
Wenn ich diese jedoch aktiviere sieht der Aufbau meiner GUI so aus:






komisch... auch verändert sich das verhalten dahingehend, dass sich bei mausbewegungen über 
dem tab-überschrift "Event Programm" sich das GUI stets updatet, was zu einem flimmern und 
der im obigen bild dargestellten verwirrung führt. bist du was java2d betrifft orientiert?


----------



## André Uhres (9. Dez 2005)

Ein kurzes, selbständiges, kompilierbares Beispiel wäre mir lieber,
aber ich schau mal was sich machen lässt.


----------



## André Uhres (9. Dez 2005)

Das folgende KSKB müsste eigentlich von der Grafik her deinen Ansprüchen genügen.
Es enthält zwar nicht deine komplizierten Algorithmen, aber darum gehts ja nicht.

```
/*
 * GrafikTest.java
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class GrafikTest extends JFrame {
    public GrafikTest() {
        super("GUIPanelGraf");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(500,300);
        setLocationRelativeTo(null);
        grafik = new GUIPanelGraf();
        add(grafik, BorderLayout.CENTER);
    }
    public static void main(String args[]) {new GrafikTest().setVisible(true);}
    private GUIPanelGraf grafik;    
}
class GUIPanelGraf extends JPanel {
    public GUIPanelGraf(){
        setLayout(null);
        fields = new JPanelTimeField[3];
        int len = fields.length;
        for (int i = 0; i < len; i++) {
            fields[i] = new JPanelTimeField(i*150+10,100,100+i*30,50, "Event "+(i+1));
            add(fields[i]);
        }
    }
    public void paintComponent(Graphics g){ //paintComponent malt nur dieses Panel
                                            //..nicht die Kind-Komponenten (fields)
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.drawLine(120,0,120,300);
        g2d.drawLine(120*2,0,120*2,300);
        g2d.drawLine(120*3,0,120*3,300);
    }
    private JPanelTimeField[] fields;
}
class JPanelTimeField extends JPanel {
    public JPanelTimeField(int nX, int nY, int nXWidth, int nYHeight, String nameIn) {
        name = nameIn;
        setBounds(nX,nY,nXWidth,nYHeight);
        setBackground(Color.blue);
        setBorder(new BevelBorder(BevelBorder.RAISED, Color.gray, Color.darkGray));
        addMouseListener(new MouseAdapter(){
            public void mouseClicked(MouseEvent e){
                System.out.println(name);
            }
        });
    }
    private String name;
}
```


----------



## KS (10. Dez 2005)

also, vielen Dank für deine Mühe. Sehr saubere Ausführung. Ich habe es nun auch hin bekommen. Schlüssel aus dem Chaos war die Zeile: 

super.paintComponent(g); //Deine Zeile 32

Nach dieser Änderung wurde das GUI sauber dargestellt. Kannst Du mir erklären, was das grob bewirkt?


----------



## André Uhres (10. Dez 2005)

Durch "super.paintComponent(g)" wird das off-screen Bild bereingt.
Fehlt diese Anweisung, dann kann es sein, daß das Bild nicht sauber rauskommt.


----------

