Subtrees hinzugefügt

This commit is contained in:
Thomas Schaller 2024-12-29 12:45:17 +01:00
parent 8ea74df22b
commit a6a8c6d8c1
28 changed files with 2731 additions and 0 deletions

View file

@ -0,0 +1,5 @@
*.class
*.ctxt
*.sh
repo.adoc
*.~lock

View file

@ -0,0 +1,117 @@
import java.io.ByteArrayOutputStream;
import java.util.Random;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
/**
*
* Hilfsklasse zur Konvertierung in Base64 von Strings
* @author Emil Hernvall (https://gist.github.com/emilhernvall/953733)
*/
public class Base64
{
public static String encode(String text)
{
byte[] data = text.getBytes();
return encode(data);
}
public static String encode(byte[] data) {
char[] tbl = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
StringBuilder buffer = new StringBuilder();
int pad = 0;
for (int i = 0; i < data.length; i += 3) {
int b = ((data[i] & 0xFF) << 16) & 0xFFFFFF;
if (i + 1 < data.length) {
b |= (data[i+1] & 0xFF) << 8;
} else {
pad++;
}
if (i + 2 < data.length) {
b |= (data[i+2] & 0xFF);
} else {
pad++;
}
for (int j = 0; j < 4 - pad; j++) {
int c = (b & 0xFC0000) >> 18;
buffer.append(tbl[c]);
b <<= 6;
}
}
for (int j = 0; j < pad; j++) {
buffer.append("=");
}
return buffer.toString();
}
public static String decodeStr(String data)
{
return new String(decode(data));
}
public static byte[] decode(String data)
{
int[] tbl = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
byte[] bytes = data.getBytes();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
for (int i = 0; i < bytes.length; ) {
int b = 0;
if (tbl[bytes[i]] != -1) {
b = (tbl[bytes[i]] & 0xFF) << 18;
}
// skip unknown characters
else {
i++;
continue;
}
int num = 0;
if (i + 1 < bytes.length && tbl[bytes[i+1]] != -1) {
b = b | ((tbl[bytes[i+1]] & 0xFF) << 12);
num++;
}
if (i + 2 < bytes.length && tbl[bytes[i+2]] != -1) {
b = b | ((tbl[bytes[i+2]] & 0xFF) << 6);
num++;
}
if (i + 3 < bytes.length && tbl[bytes[i+3]] != -1) {
b = b | (tbl[bytes[i+3]] & 0xFF);
num++;
}
while (num > 0) {
int c = (b & 0xFF0000) >> 16;
buffer.write((char)c);
b <<= 8;
num--;
}
i += 4;
}
return buffer.toByteArray();
}
}

View file

@ -0,0 +1,36 @@
import java.awt.Color;
/**
*
* Hilfsklasse für ein Buchstaben-Element (varibaler Buchstabe)
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class BuchstabeElement extends Element {
public BuchstabeElement() {
super();
super.setText("A");
}
public void setText(String t) {
super.setText("A"); // kann man nicht ändern
}
public Color getColor() {
return Color.blue;
}
public Element getCopy() {
BuchstabeElement e = new BuchstabeElement();
return e;
}
public String getXMLIdentifier() {
return "buchstabe";
}
}

View file

@ -0,0 +1,555 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import java.io.File;
import java.awt.image.BufferedImage;
import java.math.BigInteger;
import javax.swing.border.*;
import javax.swing.text.*;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
/**
*
* Chat-Programm unter Verwendung eines Protokolls
*
*
* @version 1.2 vom 08.11.2019
* 1.2: Eingabefelder verschwinden nicht mehr wegen Zeilenumbruch, UTF8 korrekt
* @author Thomas Schaller
*/
public class ChatClient extends JFrame implements MySocketListener {
//
protected int HOEHE = 60; // Höhe einer Nachricht
// Anfang Attribute
protected JPanel jPanel1 = new JPanel(null, true);
protected JPanel jPanel2 = new JPanel(null, true);
protected JLabel jLabel2 = new JLabel();
protected JTextField jTFName = new JTextField();
protected JTextField jTFAdress = new JTextField();
protected JLabel jLabel3 = new JLabel();
protected JButton jBVerbinden = new JButton();
protected JLabel jLabel1 = new JLabel();
protected JTextField jTFNachricht = new JTextField();
protected MySocket client = null;
protected JLabel jLabel5 = new JLabel();
protected JScrollPane jScrollPane1 = new JScrollPane();
protected JPanel jp;
protected ArrayList<Nachricht> nachrichten;
protected ArrayList<String> kontakte;
protected JLabel jLabel4 = new JLabel();
protected GridBagLayout gbl;
protected int keyColor = 0;
private JLabel jLabel6 = new JLabel();
protected JScrollPane jScrollPane2 = new JScrollPane();
private MessageChoosePanel messageChoosePanel1 = new MessageChoosePanel();
private JComboBox<String> jCBEmpfaenger = new JComboBox<String>();
private DefaultComboBoxModel<String> jCBEmpfaengerModel = new DefaultComboBoxModel<String>();
private JPanel jPNachricht = new JPanel(null, true);
private JLabel jLabel7 = new JLabel();
private JButton jBSend = new JButton();
private JButton jBProtokollLaden = new JButton();
private JComboBox<String> jCBLevel = new JComboBox<String>();
private DefaultComboBoxModel<String> jCBLevelModel = new DefaultComboBoxModel<String>();
private JFileChooser chooser = new JFileChooser();
// Ende Attribute
public ChatClient(String title) {
// Frame-Initialisierung
super(title);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
addWindowListener(new WindowListener() {
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowClosing(WindowEvent e) {
if (client != null) {
client.sendeNachricht("exit");
client.trenneVerbindung();
} // end of if
}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
});
int frameWidth = 1081;
int frameHeight = 683;
setSize(frameWidth, frameHeight);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
setResizable(false);
Container cp = getContentPane();
cp.setLayout(null);
// Anfang Komponenten
// ----------------------- Verbindung zu Server -----------------------------------
jPanel1.setBounds(8, 8, 600, 41);
jPanel1.setOpaque(false);
jPanel1.setBorder(BorderFactory.createBevelBorder(1, Color.WHITE, Color.DARK_GRAY));
cp.add(jPanel1);
jLabel2.setBounds(244, 11, 62, 20);
jLabel2.setText("Server");
jPanel1.add(jLabel2);
jTFName.setBounds(60, 11, 150, 20);
jTFName.setText("anonymous");
jPanel1.add(jTFName);
jTFAdress.setBounds(292, 11, 150, 20);
jTFAdress.setText("localhost");
jPanel1.add(jTFAdress);
jLabel1.setBounds(8, 12, 62, 18);
jLabel1.setText("Name");
jPanel1.add(jLabel1);
jBVerbinden.setBounds(486, 8, 99, 25);
jBVerbinden.setText("Verbinden");
jBVerbinden.setMargin(new Insets(2, 2, 2, 2));
jBVerbinden.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBVerbinden_ActionPerformed(evt);
}
});
jPanel1.add(jBVerbinden);
// -------------------------- Protokoll ------------------------------------------------
jPanel2.setBounds(618, 8, 430, 41);
jPanel2.setOpaque(false);
jPanel2.setBorder(BorderFactory.createBevelBorder(1, Color.WHITE, Color.DARK_GRAY));
cp.add(jPanel2);
jCBLevelModel = new DefaultComboBoxModel();
jCBLevelModel.addElement("Standardmodus (Normal)");
jCBLevelModel.addElement("Nachrichten können verloren gehen (Mittel)");
jCBLevelModel.addElement("Nachrichten können Bitfehler haben (Schwer)");
jCBLevel.setModel(jCBLevelModel);
jCBLevel.setBounds(10, 8, 280, 30);
jPanel2.add(jCBLevel);
jBProtokollLaden.setBounds(300, 8, 120, 25);
jBProtokollLaden.setText("Protokoll laden");
jBProtokollLaden.setMargin(new Insets(2, 2, 2, 2));
jBProtokollLaden.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBProtokollLaden_ActionPerformed(evt);
}
});
jPanel2.add(jBProtokollLaden);
jLabel5.setBounds(8, 64, 121, 80);
jLabel5.setText("<html>Nachrichten<br>laut Protokoll</html>");
jLabel5.setVerticalAlignment(JLabel.TOP);
cp.add(jLabel5);
jLabel4.setBounds(8, 296, 86, 20);
jLabel4.setText("Verlauf");
cp.add(jLabel4);
jLabel6.setBounds(816, 624, 229, 20);
jLabel6.setText("(cc) 2019, Thomas Schaller, Version 1.2");
jLabel6.setForeground(Color.GRAY);
cp.add(jLabel6);
messageChoosePanel1.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
messageChoosePanel1_MouseClicked(evt);
}
});
jScrollPane2 = new JScrollPane(messageChoosePanel1);
jScrollPane2.setBounds(144, 64, 905, 161);
jScrollPane2.setBorder(new javax.swing.border.LineBorder(Color.DARK_GRAY, 1));
cp.add(jScrollPane2);
// ------------------- Nachricht -----------------------------
jLabel7.setBounds(8, 248, 110, 20);
jLabel7.setText("Nachricht");
cp.add(jLabel7);
JPanel rahmen = new JPanel();
rahmen.setBounds(144,240,905,45);
rahmen.setBorder(new javax.swing.border.LineBorder(Color.DARK_GRAY, 1));
rahmen.setLayout(null);
cp.add(rahmen);
kontakte = new ArrayList<String>();
kontakte.add("alle");
jCBEmpfaengerModel = new DefaultComboBoxModel(kontakte.toArray());
jCBEmpfaenger.setModel(jCBEmpfaengerModel);
jCBEmpfaenger.setBounds(624, 8, 161, 30);
rahmen.add(jCBEmpfaenger);
jPNachricht.setBounds(10, 8, 609, 30);
jPNachricht.setOpaque(false);
jPNachricht.setBorder(new LineBorder(Color.BLACK, 0));
rahmen.add(jPNachricht);
jBSend.setBounds(800, 8, 99, 30);
jBSend.setText("Abschicken");
jBSend.setMargin(new Insets(2, 2, 2, 2));
jBSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBSend_ActionPerformed(evt);
}
});
rahmen.add(jBSend);
jTFNachricht.setBounds(10, 20, 358, 20);
nachrichten = new ArrayList<Nachricht>();
jp = new JPanel();
jScrollPane1 = new JScrollPane(jp);
jScrollPane1.setBounds(144, 296, 905, 321);
jScrollPane1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane1.setBorder(new javax.swing.border.LineBorder(Color.DARK_GRAY, 1));
cp.add(jScrollPane1);
gbl = new GridBagLayout();
jp.setLayout(gbl);
this.updateNachrichtenListe();
// Ende Komponenten
// File-Choose Dialog
chooser.setCurrentDirectory(new File("."));
FileFilter filter = new FileNameExtensionFilter("XML-Dateien", "xml");
// Filter wird unserem JFileChooser hinzugefügt
chooser.addChoosableFileFilter(filter);
chooser.setAcceptAllFileFilterUsed(false);
setVisible(true);
} // end of public ChatClient
// Anfang Methoden
//-------------------------------------------------------------- XML - Protokoll -------------------------------------------------------------------------
/** Laden eines vorher definierten Protokolls aus XML-Datei
*/
public void jBProtokollLaden_ActionPerformed(ActionEvent evt) {
// Dialog zum Oeffnen von Dateien anzeigen
int rueckgabeWert =chooser.showDialog(null, "Protokoll laden");
/* Abfrage, ob auf "Öffnen" geklickt wurde */
if(rueckgabeWert == JFileChooser.APPROVE_OPTION)
{
readXMLFile( chooser.getSelectedFile().getAbsolutePath());
}
}
/** XML-File verarbeiten => Nachrichten hinzufügen
*/
public void readXMLFile(String dateiname) {
XMLDokument f = new XMLDokument(dateiname);
if (!f.getName().equals("protokoll")) return; // ist es wirklich eine Protokolldefinition
jLabel5.setText("<html>Nachrichten<br>laut Protokoll:<br>"+f.getAttributwert("name")+"</html>");
boolean ok = f.stepInto();
messageChoosePanel1.deleteAllMessages();
while (ok) {
if (f.getName().equals("nachricht")) { // in oberste Ebene müssen "nachrichten" Elemente stehen
Message m = new Message();
boolean ok2 = f.stepInto(); // eintauchen in nachrichten-Element (kann text, buchstabe und ziffer enthalten)
while (ok2) { // gibt es weitere Unterelemente?
if(f.getName().equals("text")) {
TextElement t = new TextElement();
t.setText(f.getInhalt());
m.add(t);
}
if(f.getName().equals("buchstabe")) {
m.add(new BuchstabeElement());
}
if(f.getName().equals("ziffer")) {
m.add(new ZifferElement());
}
ok2 = f.step(); // nächstes Unterelement
} // end of while
f.stepOut(); // wieder auf Nachrichtenebene zurück
messageChoosePanel1.add(m);
}
ok = f.step();
} // end of while
messageChoosePanel1.invalidate();
messageChoosePanel1.repaint();
}
// --------------------------------------------------------- Client-Server Verbindung ---------------------------------------------------
/** Methode zum Verbinden mit einem Server
*/
public void jBVerbinden_ActionPerformed(ActionEvent evt) {
String a = jTFAdress.getText();
int p = 44444;
if (client != null && client.isAktiv()) {
jTFName.setEnabled(true);
jTFAdress.setEnabled(true);
jBVerbinden.setText("Verbinden");
client.sendeNachricht("exit");
client.trenneVerbindung();
} else {
client = new MySocket(a, p, this);
if (client.isAktiv()) {
jTFName.setEnabled(false);
jTFAdress.setEnabled(false);
jBVerbinden.setText("Trennen");
client.sendeNachricht(("user:"+jTFName.getText()+":alle"));
}
} // end of if-else
} // end of jBVerbinden_ActionPerformed
/** Neuer Client macht nichts
*/
public void neuerClient(MySocket client) {
}
public void nachrichtEmpfangen(MySocket client) {
String s = client.holeNachricht();
String[] ss = s.split(":");
if (ss[0].equals("user")) {
kontakte.add(ss[1]);
Nachricht n = new Nachricht("Server","alle","Neuer User: "+ss[1]);
n.setSended(true);
nachrichten.add(0,n);
this.updateNachrichtenListe();
DefaultComboBoxModel m = new DefaultComboBoxModel(kontakte.toArray());
jCBEmpfaenger.setModel(m);
}
if(ss[0].equals("mess")) {
Nachricht n = new Nachricht(ss[1],ss[2],Base64.decode(ss[3]));
nachrichten.add(0,n);
this.updateNachrichtenListe();
} // end of if
}
public void verbindungBeendet(MySocket client) {
jTFName.setEnabled(true);
jTFAdress.setEnabled(true);
jBVerbinden.setText("Verbinden");
client = null;
}
/** Aktualisiert die empfangenen Nachrichten
*/
public void updateNachrichtenListe() {
jp.setBounds(jScrollPane1.getViewport().getBounds());
GridBagConstraints c = new GridBagConstraints();
while (jp.getComponentCount()>1) {
jp.remove(1);
} // end of while
jp.removeAll();
c.weightx=0;
c.gridwidth=1;
int xx = 0;
JLabel name = null;
for (Nachricht n : nachrichten) {
name = new JLabel();
name.setText("<html>Von "+n.getAbsender()+"</html>");
name.setBorder(BorderFactory.createBevelBorder(0));
name.setBackground(new Color(250,255,200));
name.setOpaque(true);
c.gridx=0; c.gridy = xx;
c.fill = GridBagConstraints.BOTH;
jp.add(name,c);
JPanel nnn = new JPanel();
nnn.setLayout(new GridBagLayout());
nnn.setBorder(BorderFactory.createBevelBorder(0));
nnn.setBackground(new Color(250,255,200));
nnn.setOpaque(true);
JLabel nachricht = new JLabel();
if (n.getInhaltString().trim().length()>65) {
nachricht.setText("<html>"+n.getInhaltString().trim().substring(0,62)+"...</html>");
} else {
nachricht.setText("<html>"+n.getInhaltString().trim()+"</html>");
} // end of if-else
c.gridx=0; c.gridy = 0;
c.weightx=1.0; c.weighty= 0;
nnn.add(nachricht,c);
c.gridx=1; c.gridy = xx;
c.weightx = 1.0;
jp.add(nnn,c);
JPanel abspanel = new JPanel();
abspanel.setPreferredSize(new Dimension(150,HOEHE));
abspanel.setBorder(BorderFactory.createBevelBorder(0));
abspanel.setBackground(new Color(250,255,200));
abspanel.setOpaque(true);
abspanel.setLayout(new FlowLayout());
abspanel.add(new JLabel("an "+n.getEmpfaenger()));
c.gridx=2; c.gridy = xx;
c.weightx = 0.0;
jp.add(abspanel,c);
xx++;
}
JPanel dummy = new JPanel();
dummy.setBorder(BorderFactory.createBevelBorder(0));
dummy.setLayout(null);
dummy.setBackground(new Color(250,255,200));
dummy.setOpaque(true);
dummy.setPreferredSize(new Dimension(150,HOEHE));
c.gridx=0; c.gridy = xx;
c.gridwidth=4;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
jp.add(dummy,c);
jp.revalidate();
jp.repaint();
}
/** Nachricht in Nachrichtenvorrat ausgewählt, im Sendenfenster wird eine Nachricht vorbereitet (incl. Eingabefelder)
*/
public void messageChoosePanel1_MouseClicked(MouseEvent evt) {
Message m = messageChoosePanel1.getElement(evt);
if (m==null) {
// System.out.println("nicht getroffen");
} else {
jPNachricht.setLayout(new BoxLayout(jPNachricht, BoxLayout.LINE_AXIS));
JLabel l = new JLabel();
l.setText("");
l.setBounds(10,2,2,20);
l.setOpaque(true);
jPNachricht.add(l);
JPanel ml = new JPanel();
Font fo = new Font("SansSerif", Font.BOLD, 12);
ml.setBackground(new Color(50,200,50));
ml.setBorder(new TextBubbleBorder(ml.getBackground(),2,6,0));
ml.setOpaque(true);
FlowLayout flow = new FlowLayout();
flow.setHgap(3);
flow.setVgap(2);
flow.setAlignment(FlowLayout.LEFT );
ml.setLayout(flow);
int breite = 10;
for (int i=0;i<m.size() ; i++ ) {
Element e = m.get(i);
JComponent c = new JLabel();
if (e instanceof TextElement) {
c = new JLabel();
((JLabel) c).setText(((TextElement) e).getText());
c.setOpaque(true);
c.setBackground(e.getColor());
c.setForeground(Color.white);
c.setBorder(new TextBubbleBorder(e.getColor(), 3, 3, 0));
} // end of if
if (e instanceof BuchstabeElement) {
c = new JTextField();
((JTextField) c).setHorizontalAlignment(SwingConstants.CENTER);
((JTextField) c).setDocument(new MyTextFieldDocument(2));
((JTextField) c).setText("");
c.setPreferredSize(new Dimension(22,22));
c.setBorder(new TextBubbleBorder(e.getColor(), 3, 3, 0));
} // end of if
if (e instanceof ZifferElement) {
c = new JNumberField();
((JNumberField) c).setDocument(new MyTextFieldDocument(2));
((JNumberField) c).setText("0");
((JNumberField) c).setHorizontalAlignment(SwingConstants.CENTER);
c.setPreferredSize(new Dimension(22,22));
c.setBorder(new TextBubbleBorder(e.getColor(), 3, 3, 0));
} // end of if
c.setFont(fo);
ml.add(c);
breite += c.getPreferredSize().getWidth()+7;
}
JLabel filler = new JLabel();
filler.setPreferredSize(new Dimension(5,10));
ml.add(filler);
ml.setPreferredSize(new Dimension(breite,10));
// end of for
jPNachricht.removeAll();
jPNachricht.add(ml);
Dimension minSize = new Dimension(0, 5);
Dimension prefSize = new Dimension(500, 5);
Dimension maxSize = new Dimension(500, 5);
jPNachricht.add(new Box.Filler(minSize, prefSize, maxSize));
jPNachricht.revalidate();
jPNachricht.repaint();
}
} // end of messageChoosePanel1_MouseClicked
public void jBSend_ActionPerformed(ActionEvent evt) {
if (client != null && client.isAktiv()) {
int level = jCBLevel.getSelectedIndex();
Random r = new Random();
JPanel nn = (JPanel) jPNachricht.getComponent(0);
String text = "";
String text_uebertragen = "";
for (Component c : nn.getComponents() ) {
if (c instanceof JLabel) {
text+= ((JLabel) c).getText();
} // end of if
if (c instanceof JTextField) {
text+= ((JTextField) c).getText();
} // end of if
text += " ";
} // end of for
if (level ==2 && r.nextDouble()>0.6) { // Übertragungsfehler einbauen
int pos = r.nextInt(text.length());
char c = text.charAt(pos);
if (c >= 'A' && c <= 'Z') {
c = (char) (r.nextInt(26)+65);
}
if (c >= 'a' && c <= 'z') {
c = (char) (r.nextInt(26)+97);
}
if (c >= '0' && c <= '9') {
c = (char) (r.nextInt(10)+48);
}
// end of if-else
text_uebertragen = text.substring(0,pos)+ c+text.substring(pos+1);
} else {
text_uebertragen = text;
} // end of if-else
Nachricht n = new Nachricht(jTFName.getText(), (String) jCBEmpfaenger.getSelectedItem(), text);
Nachricht n2 = new Nachricht(jTFName.getText(), (String) jCBEmpfaenger.getSelectedItem(), text_uebertragen);
String b = "mess:"+n2.getAbsender()+":"+n2.getEmpfaenger()+":"+Base64.encode(n2.getInhalt());
if (level < 1 || r.nextDouble() < 0.75) { // Nachricht kann verloren gehen bei Level 2
client.sendeNachricht(b);
}
nachrichten.add(0,n);
this.updateNachrichtenListe();
} else {
jTFName.setEnabled(true);
jTFAdress.setEnabled(true);
jBVerbinden.setEnabled(true);
} // end of if
} // end of jBSend_ActionPerformed
// Ende Methoden
public static void main(String[] args) {
new ChatClient("ChatClient");
} // end of main
} // end of class ChatClient

View file

@ -0,0 +1,252 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.ArrayList;
import java.net.InetAddress;
/**
*
* Chat-Server-Programm: Die Nachrichten werden einfach an alle Teilnehmer weitergegeben
*
*
* @version 1.2 vom 08.11.2019
* 1.2: IP-Adresse wird angezeigt
* @author Thomas Schaller
*/
public class ChatServerGUI extends JFrame implements MyServerSocketListener {
// Anfang Attribute
private ArrayList<MySocket> clients;
private ArrayList<String> names;
private static boolean ende;
private MyServer server;
private JLabel jLabel1 = new JLabel();
private JButton jBStarten = new JButton();
private JTextField jTFIP = new JTextField();
private JList jList1 = new JList();
private DefaultListModel jList1Model = new DefaultListModel();
private JScrollPane jList1ScrollPane = new JScrollPane(jList1);
private JLabel jLabel2 = new JLabel();
private int changeClientListAktiv = 0;
private JLabel jLabel3 = new JLabel();
// Ende Attribute
public ChatServerGUI() {
// Frame-Initialisierung
super();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
int frameWidth = 300;
int frameHeight = 311;
setSize(frameWidth, frameHeight);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
setTitle("Chat-Server");
setResizable(false);
Container cp = getContentPane();
cp.setLayout(null);
// Anfang Komponenten
jLabel1.setBounds(8, 16, 110, 20);
jLabel1.setText("Server-Adresse");
cp.add(jLabel1);
jTFIP.setBounds(110, 16, 165, 20);
try {
jTFIP.setText(""+InetAddress.getLocalHost());
}
catch (Exception e) {
}
jTFIP.setEditable(false);
cp.add(jTFIP);
jBStarten.setBounds(96, 208, 75, 25);
jBStarten.setText("Starten");
jBStarten.setMargin(new Insets(2, 2, 2, 2));
jBStarten.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBStarten_ActionPerformed(evt);
}
});
cp.add(jBStarten);
jList1.setModel(jList1Model);
jList1ScrollPane.setBounds(8, 62, 270, 137);
cp.add(jList1ScrollPane);
jLabel2.setBounds(8, 38, 110, 20);
jLabel2.setText("Chatteilnehmer");
cp.add(jLabel2);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
ChatServerGUI_WindowClosing(evt);
}
});
jLabel3.setBounds(24, 248, 229, 20);
jLabel3.setText("(cc) 2019, Thomas Schaller, Version 1.2");
jLabel3.setForeground(Color.GRAY);
cp.add(jLabel3);
// Ende Komponenten
setVisible(true);
clients = new ArrayList<MySocket>();
names = new ArrayList<String>();
server = null;
} // end of public ChatServerGUI
// Anfang Methoden
public void neuerClient(MySocket client) {
for (String n : names) {
if (!n.equals("ManInTheMiddle")) {
client.sendeNachricht("user:"+n);
} // end of if
} // end of for
this.clients.add(client);
// System.out.println("Habe Kontakt mit "+client.getSocket().getInetAddress()+" Port "+client.getSocket().getPort());
this.names.add("?");
// changeClientList();
}
public void changeClientList() {
changeClientListAktiv++;
if (changeClientListAktiv == 1) {
do{
changeClientListAktiv--;
DefaultListModel newListModel = new DefaultListModel();
System.out.println("lm: "+jList1Model.size());
System.out.println("Clients: "+clients.size());
for (int i = 0; i < clients.size() ; i++ ) {
MySocket s = clients.get(i);
String n = names.get(i);
newListModel.addElement(n+"("+s.getSocket().getInetAddress()+":"+s.getSocket().getPort()+")");
System.out.println(n+"("+s.getSocket().getInetAddress()+":"+s.getSocket().getPort()+")");
System.out.println("lm: "+jList1Model.size());
} // end of for
jList1.setModel(newListModel);
jList1.validate();
}
while (changeClientListAktiv > 0 );
} // end of if
}
public void nachrichtEmpfangen(MySocket client) {
String s = client.holeNachricht();
System.out.println("Habe Nachricht empfangen:"+s);
String[] ss = s.split(":");
int i = clients.indexOf(client);
if (ss[0].equals("exit")) {
s = s+names.get(i);
clients.remove(i);
names.remove(i);
client.trenneVerbindung();
for (int j=0; j<clients.size(); j++) {
MySocket c = clients.get(j);
if (c!=client) {
c.sendeNachricht(s);
} // end of if
}
changeClientList();
return;
}
if (ss[0].equals("user")) {
names.set(i,ss[1]);
for (int j=0; j<clients.size(); j++) {
MySocket c = clients.get(j);
if (c!=client) {
c.sendeNachricht(s);
} // end of if
}
changeClientList();
return;
}
if (ss[0].equals("mitm")) {
names.set(i,"ManInTheMiddle");
changeClientList();
return;
}
if (ss[0].equals("mess")) {
int nextMitM=-1;
int start=0;
if (names.get(i).equals("ManInTheMiddle")) {
start = i+1;
}
for (int j=start;j<clients.size() ;j++ ) {
if (names.get(j).equals("ManInTheMiddle")) {
nextMitM = j;
break;
} // end of if
} // end of for
if (nextMitM!=-1) {
MySocket c = clients.get(nextMitM);
c.sendeNachricht(s);
} else {
for (int j=0; j<clients.size(); j++) {
MySocket c = clients.get(j);
if (!ss[1].equals(names.get(j)) && (ss[2].equals("alle") || ss[2].equals(names.get(j))) && !names.get(j).equals("ManInTheMiddle")) {
c.sendeNachricht(s);
} // end of if
}
} // end of if-else
} // end of if
} // end of if
public void verbindungBeendet(MySocket client) {
if (clients.contains(client)) {
int i = clients.indexOf(client);
clients.remove(i);
System.out.println("Verbindung mit "+client.getSocket().getInetAddress()+" Port: "+client.getSocket().getPort()+" verloren.");
for (MySocket c: clients) {
if (c!=client) {
c.sendeNachricht(("exit:"+names.get(i)+":alle"));
} // end of if
}
names.remove(i);
changeClientList();
} // end of if
}
public static void main(String[] args) {
new ChatServerGUI();
} // end of main
public void jBStarten_ActionPerformed(ActionEvent evt) {
if (jBStarten.getText().equals("Starten")) {
server = new MyServer(this, 44444);
server.starten();
jBStarten.setText("Stoppen");
} else {
server.stoppen();
server = null;
jBStarten.setText("Starten");
} // end of if-else
} // end of jBStarten_ActionPerformed
public void ChatServerGUI_WindowClosing(WindowEvent evt) {
if (server != null) server.stoppen();
} // end of ChatServerGUI_WindowClosing
// Ende Methoden
} // end of class ChatServerGUI

View file

@ -0,0 +1,54 @@
import java.awt.Color;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Oberklasse für alle Elemente einer Nachricht
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public abstract class Element {
private String text="";
public void setText(String t) {
text = t.toUpperCase();
}
public String getText() {
return text;
}
public abstract Color getColor();
public void paint(Graphics g, int x, int y, Font fo) {
Graphics2D g2 = (Graphics2D) g;
int w = g.getFontMetrics(fo).stringWidth(getText());
g2.setColor(getColor().darker());
g2.fillRoundRect(x+1,y+2,w+12,20,8,10);
g2.setColor(getColor());
g2.fillRoundRect(x,y,w+12,20,8,10);
g2.setColor(Color.white);
g2.setFont(fo);
g2.drawString(getText(),x+6,y+15);
}
public int getWidth(Graphics g, Font fo) {
return g.getFontMetrics(fo).stringWidth(getText());
}
public int getHeight(Graphics g, Font fo) {
return g.getFontMetrics(fo).getHeight();
}
public abstract Element getCopy();
public abstract String getXMLIdentifier();
}

View file

@ -0,0 +1,34 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Hilfsklasse die Anzeige eines Nachrichten-Elements für den Vorrat aus dem man auswählen kann.
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class ElementPanel extends JPanel {
private Element e;
private Font fo;
public ElementPanel(Element e) {
this.fo = new Font("SansSerif", Font.BOLD, 12);
this.e = e;
}
public void paint(Graphics g) {
e.paint(g, 0, 0, fo);
}
public Element getElement() {
return e;
}
}

View file

@ -0,0 +1,69 @@
import java.util.ArrayList;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Eine Nachricht aus mehereren Elementen
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class Message {
private ArrayList<Element> elemente = new ArrayList<Element>();
public void add(Element e) {
elemente.add(e);
}
public void delete(int i) {
elemente.remove(i);
}
public void delete(Element e) {
elemente.remove(e);
}
public Element get(int i) {
return elemente.get(i);
}
public void addBefore(Element e, Element e2) {
elemente.add(elemente.indexOf(e2),e);
}
public int size() {
return elemente.size();
}
public void paint(Graphics g, int x, int y, Font fo) {
Graphics2D g2 = (Graphics2D) g;
int p = x;
for (Element e: elemente) {
e.paint(g, p, y, fo);
p+=e.getWidth(g, fo)+15;
} // end of for
}
public int getWidth(Graphics g, Font fo) {
int b = -8;
for (Element e: elemente) {
b += e.getWidth(g, fo)+15;
} // end of for
return b;
}
public int getHeight(Graphics g, Font fo) {
return g.getFontMetrics(fo).getHeight()+12;
}
}

View file

@ -0,0 +1,67 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.ArrayList;
/**
*
* Panel zur Anzeige mehrerer Nachrichten.
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class MessageChoosePanel extends JPanel {
private Color f;
private Font fo;
private ArrayList<Message> ml;
public MessageChoosePanel() {
this.f = new Color(50,200,50);
this.fo = new Font("SansSerif", Font.BOLD, 12);
this.ml = new ArrayList<Message>();
}
public void add(Message nm) {
ml.add(nm);
setPreferredSize(new Dimension(500, ml.size()*40));
invalidate();
}
public void deleteAllMessages() {
ml.clear();
invalidate();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
int y = 5;
for (Message m: ml) {
int b = m.getWidth(g, fo);
g2.setColor(f.darker());
g2.fillRoundRect(12,y+2,b+12,30,8,10);
g2.setColor(f);
g2.fillRoundRect(10,y,b+12,30,8,10);
m.paint(g, 14, y+4, fo);
y += m.getHeight(g, fo)+7;
} // end of for
}
// Nachricht bestimmen, die an bestimmter Position angezeigt wird.
public Message getElement(MouseEvent evt) {
if (evt.getY() / 35 >= ml.size()) {
return null;
} // end of if
return ml.get(evt.getY() / 35);
}
}

View file

@ -0,0 +1,62 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Panel zur Anzeige einer einzigen Nachricht
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class MessagePanel extends JPanel {
private Color f;
private Font fo;
private Message m;
public MessagePanel( Message m) {
this.f = new Color(50,200,50);
this.fo = new Font("SansSerif", Font.BOLD, 12);
this.m = m;
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
int b = m.getWidth(g, fo);
if (b < 50) b = 50;
g2.setColor(f.darker());
g2.fillRoundRect(2,2,b+12,30,8,10);
g2.setColor(f);
g2.fillRoundRect(0,0,b+12,30,8,10);
m.paint(g, 4, 4, fo);
}
// Bestimmt das Element einer Nachricht an einer bestimmten Position
public Element getElement(Point evt) {
if (evt.getY()>4 && evt.getY()<26) {
int p=1;
for (int i=0; i<m.size() ;i++ ) {
int w = getFontMetrics(fo).stringWidth(m.get(i).getText())+12;
if (evt.getX()>=p && evt.getX() < p+w+3) {
return m.get(i);
} // end of if
p += w+3;
} // end of for
} // end of if
return null;
}
public Message getMessage() {
return m;
}
}

View file

@ -0,0 +1,67 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JOptionPane;
import javax.swing.*;
/**
*
* Kontextmenü, wenn auf einzelnes Element geklickt wurde
*
*
* @version 1.1 vom 14.06.2018
* @author Thomas Schaller
*/
public class MessagePopup extends JPopupMenu implements ActionListener {
private Element e;
private Message m;
private JMenuItem dmi1, dmi2, dmi3;
private JPanel p;
private ProtokollErstellen parent;
public MessagePopup( MouseEvent event, Message m, Element e, JPanel p, ProtokollErstellen parent){
this.e = e;
this.m = m;
this.p = p;
this.parent = parent;
dmi2 = new JMenuItem("Text bearbeiten");
dmi2.addActionListener(this);
add(dmi2);
dmi1 = new JMenuItem("Element löschen");
dmi1.addActionListener(this);
add(dmi1);
dmi3 = new JMenuItem("Nachricht löschen");
dmi3.addActionListener(this);
add(dmi3);
show(event.getComponent(), event.getX(), event.getY());
}
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == dmi1) {
m.delete(e);
p.repaint();
}
if (ae.getSource() == dmi2 && !(e instanceof ZifferElement)) {
String eingabe = (String)JOptionPane.showInputDialog(null, "Bitte gib den Text ein",
"Textbaustein", JOptionPane.QUESTION_MESSAGE,null,null,e.getText());
if (eingabe != null) e.setText(eingabe);
p.repaint();
}
if (ae.getSource() == dmi3) {
parent.delete(p);
}
}
}

View file

@ -0,0 +1,98 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Die Klasse MyServer verwaltet einen ServerSocket. Sie erstellt einen ServerSocket an einem
* bestimmten Port und wartet dort auf Clients. Es können mehrere Clients gleichzeitig angemeldet sein.
* Eine Klasse, die diesen Server benutzt, muss MyServerSocketListener implementieren. Sie wird dann
* über die Methode neuerClient informiert, wenn sich ein neuer Client am Server angemeldet hat.
* Beschreibung
*
*
* @version 1.0 vom 15.11.2012
* @author Thomas Schaller
*/
public class MyServer extends Thread {
// Anfang Attribute
private ServerSocket ss;
private int port;
private boolean aktiv = true;
private MyServerSocketListener listener;
// Ende Attribute
/** Dieser Konstruktor erzeugt einen neuen ServerSocket, startet ihn aber nicht sofort.
* @param listener Klasse, die MyServerSocketListener implementiert und dann über neue Clients und eingehende Nachrichten informiert wird.
* @param port Port, den der ServerSocket abhört.
*/
public MyServer(MyServerSocketListener listener, int port) {
this.port = port;
this.listener = listener;
try {
ss = new ServerSocket(port);
} catch(Exception e) {
System.out.println(e);
} // end of try
}
// Anfang Methoden
/** Liefert den Port, den der Server abhört.
* @return Port
*/
public int getPort() {
return port;
}
/** Startet das Abhören des Ports und das Annehmen der Clients
*/
public void starten() {
start();
}
/** Verwaltet das Annehmen der Clients (Diese Methode bitte nicht direkt aufrufen, sondern mit starten() aktivieren).
*/
public void run() {
aktiv = true;
try {
while(aktiv) {
System.out.println("Warte auf Client");
Socket s = ss.accept();
System.out.println("Client empfangen");
MySocket ms = new MySocket(s, listener);
listener.neuerClient(ms);
}
} catch(Exception e) {
System.out.println(e);
} // end of try
}
/** Bricht das Abhören des Ports ab. Es kann nicht wieder durch starten aktiviert werden. Dazu muss ein neuer Server erstellt werden.
*/
public void stoppen() {
aktiv = false;
try{
ss.close();
interrupt();
} catch( Exception e) {
System.out.println(e);
}
}
/** Meldet, ob der Server aktiv ist.
* @return true, falls der Server den Port abhört, sonst false.
*/
public boolean getAktiv() {
return aktiv;
}
// Ende Methoden
} // end of Server

View file

@ -0,0 +1,15 @@
import java.net.ServerSocket;
import java.net.Socket;
/**
* Listenerdefinitionen für MyServer und MySocket.
* Klassen, die mit diesen beiden Klassen arbeiten müssen diese Listener implementieren.
*
* @version 1.0 vom 15.11.2012
* @author Thomas Schaller
*/
interface MyServerSocketListener extends MySocketListener {
void neuerClient(MySocket s); // ein neuer Client hat sich mit dem Server verbunden.
}

View file

@ -0,0 +1,196 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.io.*;
import java.util.ArrayList;
import java.nio.charset.StandardCharsets;
/**
* Die Klasse MySocket verwaltet eine Client-Server-Verbindung. Sie wartet auf eingehende Nachrichten und
* informiert ihren Listener, wenn eine Nachricht eingangen ist. Die Nachricht kann dann beim Socket abgeholt werden.
* Außerdem können Nachrichten in Form von Strings versendet werden.
* Der Listener wird außerdem informiert, wenn die Verbindung zusammengebrochen / beendet ist.
* Der Listener muss das Interface MySocketListener implementieren.
*
*
* @version 1.0 vom 15.11.2012
* @author Thomas Schaller
*/
public class MySocket extends Thread{
// Anfang Attribute2
private boolean aktiv;
private Socket socket;
private BufferedReader eingang=null;
PrintWriter ausgang=null;
MySocketListener listener=null;
// Ende Attribute2
/** Dieser Konstruktor erstellt einen MySocket aus einem schon vorhandenen Socket und ist für die Verwendung durch MyServer gedacht.
* @param s Socket, der durch MySocket verwaltet werden soll.
* @param listener Listener, der über eingehende Nachrichten und das Beenden der Verbindung informiert wird. Der Listener muss MySocketListener implementieren.
*/
public MySocket(Socket s, MySocketListener listener){
this.socket = s;
this.listener = listener;
try {
eingang = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ausgang = new PrintWriter(socket.getOutputStream());
aktiv = true;
this.start();
} catch(Exception e) {;
System.out.println(e);
e.printStackTrace();
aktiv = false;
} // end of try
}
/** Dieser Konstruktor erstellt einen neuen Socket. Er verbindet sich mit einem Server an der angegebenen Adresse unter dem angegebenen Port. Der Listener wird
* über eingehende Nachrichten und das Beenden der Verbindung informiert.
* @param address IP-Adresse oder Domain-Name des Servers
* @param port Portnummer, die der Server abhört
* @param listener Listener, der über eingehende Nachrichten und das Beenden der Verbindung informiert wird. Der Listener muss MySocketListener implementieren.
*/
public MySocket(String address, int port, MySocketListener listener) {
this.listener = listener;
try {
this.socket = new Socket(address, port);
eingang = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ausgang = new PrintWriter(socket.getOutputStream());
aktiv = true;
this.start();
} catch(Exception e) {
System.out.println(e);
e.printStackTrace();
aktiv = false;
} // end of try
}
// Anfang Methoden2
/** Verwaltet das Annehmen der Nachrichten (Diese Methode bitte nicht direkt aufrufen, sie wird automatisch gestartet).
*/
public void run(){
try {
while (aktiv){
if (listener != null && eingang.ready()) listener.nachrichtEmpfangen(this);
Thread.sleep(5);
}
}
catch (Exception e) {
if(socket!=null)try{socket.close();}catch(Exception ex){;}
System.err.println(e);
e.printStackTrace();
}
aktiv = false;
if (listener != null) listener.verbindungBeendet(this);
}
/** Liefert die eingangene Nachricht. Diese Methode bitte nur aufrufen, wenn der Listener über den Eingang einer Nachricht informiert wurde, da die Methode sonst bis
* zur nächsten eingehenden Nachricht wartet.
* @return Nachricht
*/
public String holeNachricht() {
try{
if (aktiv && eingang.ready()) {
String s = eingang.readLine();
System.out.println(s);
byte[] b = Base64.decode(s);
/* for (int i = 0; i < b.length ; i++) {
System.out.print(b[i]);
System.out.print(" ");
} // end of for
System.out.println("");*/
return s;
} // end of if
}
catch(Exception e) {
e.printStackTrace();
return null;
}
return null;
}
/** Versendet eine Nachricht an den Server.
* @param nachricht Zu sendender Text.
*/
public void sendeNachricht(String nachricht) {
try {
//byte[] b = nachricht;
/* for (int i = 0; i < b.length ; i++) {
System.out.print(b[i]);
System.out.print(" ");
} // end of for
System.out.println("");*/
//ausgang.println(Base64.encode(nachricht));
ausgang.println(nachricht);
// System.out.println(Base64.encode(nachricht));
ausgang.flush();
} catch(Exception e) {
System.out.println(e);
e.printStackTrace();
if (listener != null) listener.verbindungBeendet(this);
aktiv = false;
}
}
/** Bricht eine bestehende Client-Server-Verbindung ab.
*/
public void trenneVerbindung() {
aktiv = false;
if (socket != null) {
try {
socket.close();
} catch(Exception e) {
System.out.println(e);
e.printStackTrace();
}
} // end of if
socket = null; // end of try
}
/** Liefert den Socket der Verbindung. Kann benutzt werden, um dann aus dem Socket weitere Informationen wie IP-Adressen oder Portnummern zu ermitteln.
* @return Socket, der Verbindung.
*/
public Socket getSocket() {
return socket;
}
/** Liefert den Status der Verbindung.
* @return true, falls die Verbindung noch aktiv ist, sonst false.
*/
public boolean isAktiv() {
return aktiv;
}
/** Räumt alles auf, damit der Thread nicht weiterläuft (bitte nicht aufrufen).
*/
public void finalize() {
// Schließen der Streams und des Sockets, wenn
// die Verbindung gelöscht wird wird
try {
trenneVerbindung();
}
catch(Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
// Ende Methoden2
}

View file

@ -0,0 +1,16 @@
import java.net.ServerSocket;
import java.net.Socket;
/**
* Listenerdefinitionen für MyServer und MySocket.
* Klassen, die mit diesen beiden Klassen arbeiten müssen diese Listener implementieren.
*
* @version 1.0 vom 15.11.2012
* @author Thomas Schaller
*/
interface MySocketListener { // Der Listener wird über folgendes informiert...
void nachrichtEmpfangen(MySocket s); // am Socket ist eine Nachricht eingegangen. Diese kann dann mit s.holeNachricht() abgeholt werden.
void verbindungBeendet(MySocket s); // die Verbindung wurde beendet oder ist zusammengebrochen.
}

View file

@ -0,0 +1,52 @@
import javax.swing.text.*;
/**
*
* Hilfsklasse zur Eingabe eines Textes mit begrenzter Länge
*
*
* @version 1.0 vom 13.04.2018
* @author Sam Hotte (https://www.java-forum.org/thema/jtextfield-eingabelaenge-beschraenken.27393/)
*/
public class MyTextFieldDocument extends PlainDocument {
private int maxLength_ = Integer.MAX_VALUE;
/**
* Erzeugt ein javax.swing.text.Document fuer das JTextField.
*
* @param maxLength
* maximale Zeichenanzahl
*/
public MyTextFieldDocument(final int maxLength) {
this.maxLength_ = maxLength;
}
/**
* Fügt den String nur dann ein, wenn die maximale Anzahl noch nicht überschritten ist.
*
* @param offs
* Offset (Position, an der der String eingefuegt werden soll)
* @param str
* einzufuegender String
* @param a
* Attribut-Set (werden hier nicht weiter beachtet)
* @throws BadLocationException
* wird "von oben" durchgereicht
* @see javax.swing.text.AttributeSet
*/
public void insertString(final int offs, final String str,
final AttributeSet a) throws BadLocationException {
if (str == null) {
return;
}
int actualLength = this.getLength();
if (actualLength + str.length() < this.maxLength_) {
super.insertString(offs, str, a);
} else {
java.awt.Toolkit.getDefaultToolkit().beep();
}
}
}

View file

@ -0,0 +1,119 @@
/**
*
* Gesendete oder empfangene Nachricth im Chat (konnte auch Verschlüsselung, wird hier nicht gebraucht
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
import java.util.ArrayList;
public class Nachricht {
// Anfang Attribute
private byte[] inhalt;
private String absender;
private String empfaenger;
private long time;
private boolean stopped;
private boolean sended;
// Ende Attribute
public Nachricht(String absender, String empfaenger, String inhalt) {
setInhalt(inhalt);
this.absender = absender;
this.empfaenger = empfaenger;
this.time = System.currentTimeMillis();
this.stopped = false;
this.sended = false;
}
public Nachricht(String absender, String empfaenger, byte[] inhalt, byte[] hashCode) {
this.inhalt = inhalt;
this.absender = absender;
this.empfaenger = empfaenger;
this.time = System.currentTimeMillis();
this.stopped = false;
this.sended = false;
}
public Nachricht(String absender, String empfaenger, byte[] inhalt) {
this.inhalt = inhalt;
this.absender = absender;
this.empfaenger = empfaenger;
this.time = System.currentTimeMillis();
this.stopped = false;
this.sended = false;
}
// Anfang Methoden
public String getInhaltString() {
return new String(inhalt);
}
public byte[] getInhalt() {
return inhalt;
}
public void setInhalt(String inhalt) {
this.inhalt = inhalt.getBytes();
}
public void setInhalt(byte[] inhalt) {
this.inhalt = inhalt;
}
public String getAbsender() {
return absender;
}
public void setAbsender(String absender) {
this.absender = absender;
}
public String getEmpfaenger() {
return empfaenger;
}
public void setEmpfaenger(String empfaenger) {
this.empfaenger = empfaenger;
}
public Nachricht copy() {
Nachricht n = new Nachricht(absender, "", inhalt);
return n;
}
public int getRestzeit() {
if (sended || stopped) {
return 0;
} // end of if
return 10 - (int) ((System.currentTimeMillis()-this.time)/1000);
}
public boolean getSended() {
return sended;
}
public void setSended(boolean s) {
sended = s;
}
public boolean getStopped() {
return stopped;
}
public void setStopped(boolean s) {
stopped = s;
}
// Ende Methoden
}

View file

@ -0,0 +1,434 @@
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.JFileChooser;
import java.io.File;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
/**
*
* Programm zur Erstellung einer Protokolldefinition
*
* @version 1.2 vom 08.11.2019
* 1.2: Protokolle mit Umlauten werden korrekt in UTF-8 gespeichert.
* @author Thomas Schaller
*/
public class ProtokollErstellen extends JFrame {
// Anfang Attribute
private JLabel jLabel3 = new JLabel();
// Elemente die einer Nachricht hinzugefügt werden können
private JPanel jPElementVorrat = new JPanel(null, true);
private ElementPanel element1 = new ElementPanel(new TextElement());
private ElementPanel element2 = new ElementPanel(new BuchstabeElement());
private ElementPanel element3 = new ElementPanel(new ZifferElement());
// Scrollpane für die kreierten Nachrichten
private JScrollPane jScrollPane1 = new JScrollPane();
private JPanel jPNachrichten = new JPanel(null, true);
// Laden- und Speichernbutton
private JButton jBLoad = new JButton();
private JButton jBSave = new JButton();
// Gerade ausgewähltes Element, das in eine Nachricht gezogen wird
private Element dragElement = null;
// Wo ging das Drag los?
private Point dragPoint = null;
private JFileChooser chooser ;
// Ende Attribute
public ProtokollErstellen (String title) {
super (title);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
int frameWidth = 577;
int frameHeight = 356;
setSize(frameWidth, frameHeight);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
Container cp = getContentPane();
cp.setLayout(null);
// Anfang Komponenten
// Element für die Nachrichten (alle können mit der Mouse auf eine Nachricht gezogen werden
{
element1.setBounds(10, 6, 50, 33);
element1.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
elementGewaehlt(evt);
}
});
element1.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
elementLosgelassen(evt);
}
});
jPElementVorrat.add(element1);
element2.setBounds(60, 6, 25, 33);
element2.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
elementGewaehlt(evt);
}
});
element2.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
elementLosgelassen(evt);
}
});
jPElementVorrat.add(element2);
element3.setBounds(88, 6, 25, 33);
element3.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
elementGewaehlt(evt);
}
});
element3.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
elementLosgelassen(evt);
}
});
jPElementVorrat.add(element3);
jPElementVorrat.setBounds(8, 8, 545, 33);
jPElementVorrat.setOpaque(false);
jPElementVorrat.setBorder(BorderFactory.createBevelBorder(1, new Color(0xC0C0C0), Color.GRAY));
cp.add(jPElementVorrat);
}
// Scrollfenster für die kreierten Nachrichten
jScrollPane1.setBounds(8, 48, 545, 217);
jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane1.setBorder(BorderFactory.createBevelBorder(0, Color.GRAY, new Color(0xC0C0C0)));
cp.add(jScrollPane1);
jScrollPane1.setViewportView(jPNachrichten);
jPNachrichten.setOpaque(false);
jPNachrichten.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
jPNachrichten_MouseClicked(evt);
}
});
// Buttons zum Speichern und Laden
jBLoad.setBounds(88, 275, 145, 25);
jBLoad.setText("Protokoll laden");
jBLoad.setMargin(new Insets(2, 2, 2, 2));
jBLoad.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBLoad_ActionPerformed(evt);
}
});
cp.add(jBLoad);
jBSave.setBounds(320, 275, 153, 25);
jBSave.setText("Protokoll speichern");
jBSave.setMargin(new Insets(2, 2, 2, 2));
jBSave.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jBSave_ActionPerformed(evt);
}
});
cp.add(jBSave);
jLabel3.setBounds(160, 305, 229, 20);
jLabel3.setText("(cc) 2019, Thomas Schaller, Version 1.2");
jLabel3.setForeground(Color.GRAY);
cp.add(jLabel3);
setTitle("Protokoll:");
// Ende Komponenten
// FileChoose Dialog
chooser = new JFileChooser();
FileFilter filter = new FileNameExtensionFilter("XML-Dateien", "xml");
// Filter wird unserem JFileChooser hinzugefügt
chooser.addChoosableFileFilter(filter);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setCurrentDirectory(new File("."));
setResizable(false);
setVisible(true);
}
// Anfang Methoden
/** Laden einer XML-Protokolldefinition
*/
public void jBLoad_ActionPerformed(ActionEvent evt) {
// Dialog zum Oeffnen von Dateien anzeigen
int rueckgabeWert =chooser.showDialog(null, "Protokoll laden");
/* Abfrage, ob auf "Öffnen" geklickt wurde */
if(rueckgabeWert == JFileChooser.APPROVE_OPTION)
{
readXMLFile( chooser.getSelectedFile().getAbsolutePath());
}
} // end of jBLoad_ActionPerformed
public void readXMLFile(String dateiname) {
XMLDokument f = new XMLDokument(dateiname);
if (!f.getName().equals("protokoll")) return; // ist es wirklich eine Protokolldefinition
setTitle("Protokoll: "+f.getAttributwert("name"));
boolean ok = f.stepInto();
jPNachrichten.removeAll();
while (ok) { // Solange weitere Einträge vorhanden sind
if (f.getName().equals("nachricht")) { // in oberste Ebene müssen "nachrichten" Elemente stehen
Message m = newMessage();
boolean ok2 = f.stepInto(); // eintauchen in nachrichten-Element (kann text, buchstabe und ziffer enthalten)
while (ok2) { // gibt es weitere Unterelemente?
if(f.getName().equals("text")) {
TextElement t = new TextElement();
t.setText(f.getInhalt());
m.add(t);
}
if(f.getName().equals("buchstabe")) {
m.add(new BuchstabeElement());
}
if(f.getName().equals("ziffer")) {
m.add(new ZifferElement());
}
ok2 = f.step(); // nächstes Unterelement
} // end of while
f.stepOut();
}
ok = f.step(); // wieder auf Nachrichtenebene zurück
} // end of while
jPNachrichten.setPreferredSize(new Dimension(600,5+jPNachrichten.getComponentCount()*40));
jPNachrichten.invalidate();
jPNachrichten.repaint();
}
/** Speichern einer XML-Protokolldefinition
*/
public void jBSave_ActionPerformed(ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
FileFilter filter = new FileNameExtensionFilter("XML-Dateien",
"xml");
// Filter wird unserem JFileChooser hinzugefügt
chooser.addChoosableFileFilter(filter);
chooser.setAcceptAllFileFilterUsed(false);
// Dialog zum Oeffnen von Dateien anzeigen
int rueckgabeWert =chooser.showDialog(null, "Protokoll speichern");
/* Abfrage, ob auf "Speichern" geklickt wurde */
if(rueckgabeWert == JFileChooser.APPROVE_OPTION)
{
writeXMLFile( chooser.getSelectedFile().getAbsolutePath(), chooser.getSelectedFile().getName());
}
} // end of jBSave_ActionPerformed
public void writeXMLFile(String dateiname, String protokollname) {
if (protokollname.contains(".")) protokollname.substring(0,protokollname.indexOf("."));
setTitle("Protokoll: "+protokollname);
XMLDokument d = new XMLDokument();
d.fuegeElementHinzu("protokoll"); // root-Element hinzufügen
d.setAttributwert("name",protokollname);
d.stepInto(); // auf Nachrichten-Ebene eintauchen
for (Component c: jPNachrichten.getComponents()) { // alle Nachrichten aus dem Nachrichtenfenster
d.fuegeElementHinzu("nachricht");
d.stepInto(); // auf Unterelemente-Ebene eintauchen
Message m = ((MessagePanel) c).getMessage();
for (int i = 0; i< m.size(); i++) {
String a = m.get(i).getXMLIdentifier();
d.fuegeElementHinzu(a);
if(a.equals("text")) d.setInhalt(m.get(i).getText()); // Bei Texten auch den Inhalt speichern
} // end of for
d.stepOut(); // auf Nachrichtenebene zurückkehren
} // end of for
d.stepOut();
if(!dateiname.endsWith(".xml")) dateiname += ".xml";
d.speichereDokument(dateiname);
}
// --------------------------------------------------- Mausaktionen -------------------------------------------------------------
/** Kontextmenü für rechte Maustaste im Nachrichtenfenster => neue Nachricht, alle Nachrichten löschen
*/
public void jPNachrichten_MouseClicked(MouseEvent evt) {
if(SwingUtilities.isRightMouseButton(evt)){
ProtokollPopup menu = new ProtokollPopup(evt, this, null);
}
} // end of jPNachrichten_MouseClicked
/** Kontextmenü für rechte Maustaste auf eine Nachricht => neue Nachricht, Nachricht löschen, Text bearbeiten
*/
public void messagePanel1_MouseClicked(MouseEvent evt) {
if(SwingUtilities.isRightMouseButton(evt)){
MessagePanel messagePanel1 = (MessagePanel) evt.getComponent();
Element e = messagePanel1.getElement(evt.getPoint());
if (e==null) { // KLick auf Nachricht, aber kein Element => neue Nachricht, Alle löschen
ProtokollPopup menu = new ProtokollPopup(evt, this, messagePanel1);
} else { // Klick auf Element der Nachricht => Element löschen, Text bearbeiten
MessagePopup menu = new MessagePopup(evt, messagePanel1.getMessage(), e, messagePanel1, this);
}
}
// end of if
} // end of messagePanel1_MouseClicked
/** Neue Nachricht erzeugen, MouseListener für Kontextmenü und Element-Schieben hinzufügen */
public Message newMessage() {
Message m = new Message();
MessagePanel messagePanel1 = new MessagePanel(m);
messagePanel1.setBounds(8, 5+jPNachrichten.getComponentCount()*40, 529, 40);
jPNachrichten.add(messagePanel1);
messagePanel1.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
messagePanel1_MouseClicked(evt);
}
});
messagePanel1.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
elementVerschieben(evt);
}
});
messagePanel1.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
elementLosgelassen(evt);
}
});
messagePanel1.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent evt) {
elementWurdeGeschoben(evt);
}
});
jPNachrichten.setPreferredSize(new Dimension(400,5+jPNachrichten.getComponentCount()*40));
jPNachrichten.invalidate();
jPNachrichten.repaint();
jScrollPane1.setViewportView(jPNachrichten);
return m;
}
/** Einzelen Nachricht löschen */
public void delete(JPanel p) {
jPNachrichten.remove(p);
for (Component c : jPNachrichten.getComponents()) {
if(c.getY() > p.getY()) {
c.setBounds(8, c.getY()-40, 529, 40);
}
} // end of for
jPNachrichten.setPreferredSize(new Dimension(400,5+jPNachrichten.getComponentCount()*40));
jPNachrichten.invalidate();
jPNachrichten.repaint();
jScrollPane1.setViewportView(jPNachrichten);
}
/** Alle Nachrichten löschen */
public void deleteAllMessages() {
jPNachrichten.removeAll();
jPNachrichten.setPreferredSize(new Dimension(400,5+jPNachrichten.getComponentCount()*40));
jPNachrichten.invalidate();
jPNachrichten.repaint();
jScrollPane1.setViewportView(jPNachrichten);
}
/** Element aus Vorrat auswählen und in Nachricht ziehen, wenn mit linker Maustaste angeklickt. */
public void elementGewaehlt(MouseEvent evt) {
if(SwingUtilities.isLeftMouseButton(evt)){
Toolkit toolkit = Toolkit.getDefaultToolkit();
dragElement = ((ElementPanel) evt.getComponent()).getElement().getCopy();
// Bild des Elements als Cursour verwenden
BufferedImage mouseImage = new BufferedImage(40,35, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = mouseImage.createGraphics();
dragElement.paint(g2,0,0,new Font("SansSerif", Font.BOLD, 12));
Cursor c = toolkit.createCustomCursor(mouseImage , new Point(5, 5), "img");
setCursor (c);
}
} // end of element2_MousePressed
/** Verschieben eines Elements, erst aus Nachricht entfernen, wenn die Verschiebung weit genug */
public void elementVerschieben(MouseEvent evt) {
if(SwingUtilities.isLeftMouseButton(evt)){
Element e = ((MessagePanel) (evt.getComponent())).getElement(evt.getPoint());
if(e != null) {
dragElement = e;
dragPoint = evt.getPoint(); // dragPoint != null => Element ist noch in Nachricht
// Bild des Elements als Cursour verwenden
Toolkit toolkit = Toolkit.getDefaultToolkit();
BufferedImage mouseImage = new BufferedImage(40,35, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = mouseImage.createGraphics();
dragElement.paint(g2,0,0,new Font("SansSerif", Font.BOLD, 12));
Cursor c = toolkit.createCustomCursor(mouseImage , new Point(5, 5), "img");
setCursor (c);
} // end of if-else
}
} // end of element2_MousePressed
/** Drag des Elements, wenn die Verschiebung weit genug (>3 Pixel), dann wird es aus der Nachricht entfernt */
public void elementWurdeGeschoben(MouseEvent evt) {
if(SwingUtilities.isLeftMouseButton(evt) && dragElement != null && dragPoint != null && (Math.abs(dragPoint.getX()-evt.getX())+Math.abs(dragPoint.getY()-evt.getY())>3)){
((MessagePanel) (evt.getComponent())).getMessage().delete(dragElement);
jPNachrichten.invalidate();
jPNachrichten.repaint();
dragPoint = null; // Element wurde aus Nachricht entfernt
}
} // end of element2_MousePressed
/** Neu Positionierung des Drag-Elements (sowohl für neue Elemente aus Vorrat als auch für Verschobene), sofern es aus der Nachricht schon entfernt wurde. */
public void elementLosgelassen(MouseEvent evt) {
if(SwingUtilities.isLeftMouseButton(evt) && dragElement != null && dragPoint == null){
Point p = evt.getPoint();
Point pp = SwingUtilities.convertPoint(evt.getComponent(), p, jPNachrichten); // Koordinaten bezogen auf neue Nachricht bestimmen
Component mp = jPNachrichten.getComponentAt(pp);
if(mp != null && mp instanceof MessagePanel) {
Point p3 = SwingUtilities.convertPoint(evt.getComponent(), p, mp); // Koordinaten bezogen auf neue Nachricht bestimmen
Element e = ((MessagePanel) mp).getElement(p3);
if(e == null) {
((MessagePanel) mp).getMessage().add(dragElement);
} else {
((MessagePanel) mp).getMessage().addBefore(dragElement,e);
} // end of if-else
} else {
Message m = newMessage();
m.add(dragElement);
} // end of if-else
jPNachrichten.invalidate();
jPNachrichten.repaint();
}
dragElement = null;
dragPoint = null;
Cursor dc = new Cursor(Cursor.DEFAULT_CURSOR);
setCursor (dc);
} // end of element2_MouseReleased
// Ende Methoden
public static void main(String[] args) {
new ProtokollErstellen("ProtokollErstellen");
}
}

View file

@ -0,0 +1,65 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JOptionPane;
import javax.swing.*;
/**
*
* Kontextmenü für Rechtsklick im Nachrichten-Erzeugungsfenster
*
*
* @version 1.1 vom 14.06.2018
* @author Thomas Schaller
*/
public class ProtokollPopup extends JPopupMenu implements ActionListener {
private JMenuItem dmi1, dmi2, dmi3;
private ProtokollErstellen parent;
private JPanel messagePanel;
public ProtokollPopup( MouseEvent event, ProtokollErstellen parent, JPanel messagePanel){
this.parent = parent;
this.messagePanel = messagePanel;
dmi1 = new JMenuItem("Neue Nachricht");
dmi1.addActionListener(this);
add(dmi1);
if(messagePanel != null) {
dmi3 = new JMenuItem("Nachricht löschen");
dmi3.addActionListener(this);
add(dmi3);
}
dmi2 = new JMenuItem("Alle Nachrichten löschen");
dmi2.addActionListener(this);
add(dmi2);
show(event.getComponent(), event.getX(), event.getY());
}
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == dmi1) {
parent.newMessage();
}
if (ae.getSource() == dmi2) {
parent.deleteAllMessages();
}
if (ae.getSource() == dmi3) {
parent.delete(messagePanel);
}
}
}

View file

@ -0,0 +1,127 @@
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
/**
*
* Runder, farbiger Rahmen um Textfelder und Eingabefelder
*
*
* @version 1.0 vom 13.04.2018
* @author Andrew Thompson (https://stackoverflow.com/questions/8462414/internal-padding-for-jtextarea-with-background-image)
*/
public class TextBubbleBorder extends AbstractBorder {
private Color color;
private int thickness = 4;
private int radii = 8;
private int pointerSize = 7;
private Insets insets = null;
private BasicStroke stroke = null;
private int strokePad;
private int pointerPad = 4;
private boolean left = true;
RenderingHints hints;
TextBubbleBorder(
Color color) {
this(color, 4, 8, 7);
}
TextBubbleBorder(
Color color, int thickness, int radii, int pointerSize) {
this.thickness = thickness;
this.radii = radii;
this.pointerSize = pointerSize;
this.color = color;
stroke = new BasicStroke(thickness);
strokePad = thickness / 2;
// hints = new RenderingHints(
// RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
//
int pad = radii + strokePad;
pad = 3;
int bottomPad = pad + pointerSize + strokePad;
insets = new Insets(3, 5, 3, 7);
}
TextBubbleBorder(
Color color, int thickness, int radii, int pointerSize, boolean left) {
this(color, thickness, radii, pointerSize);
this.left = left;
}
@Override
public Insets getBorderInsets(Component c) {
return insets;
}
@Override
public Insets getBorderInsets(Component c, Insets insets) {
return getBorderInsets(c);
}
@Override
public void paintBorder(
Component c,
Graphics g,
int x, int y,
int width, int height) {
Graphics2D g2 = (Graphics2D) g;
int bottomLineY = height - thickness - pointerSize;
RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(
0 + strokePad,
0 + strokePad,
width - thickness-2,
bottomLineY-1,
radii,
radii);
RoundRectangle2D.Double bubble2 = new RoundRectangle2D.Double(
0 + strokePad+1,
0 + strokePad+2,
width - thickness-2,
bottomLineY-1,
radii,
radii);
Area area = new Area(bubble);
Area area2 = new Area(bubble2);
// Paint the BG color of the parent, everywhere outside the clip
// of the text bubble.
Component parent = c.getParent();
if (parent!=null) {
Color bg = parent.getBackground();
Rectangle rect = new Rectangle(0,0,width, height);
Area borderRegion = new Area(rect);
borderRegion.subtract(area);
borderRegion.subtract(area2);
g2.setClip(borderRegion);
g2.setColor(bg);
g2.fillRect(0, 0, width, height);
g2.setClip(null);
}
g2.setStroke(stroke);
Rectangle rect = new Rectangle(0,0,width, height);
Area borderRegion = new Area(rect);
borderRegion.subtract(area);
g2.setClip(borderRegion);
g2.setColor(color.darker());
g2.draw(area2);
g2.setClip(null);
g2.setColor(color);
g2.draw(area);
}
}

View file

@ -0,0 +1,31 @@
import java.awt.Color;
/**
*
* Hilfsklasse für ein Text-Element (fester String)
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class TextElement extends Element {
public TextElement() {
super();
super.setText("TEXT");
}
public Color getColor() {
return new Color(200,50,50);
}
public Element getCopy() {
TextElement e = new TextElement();
e.setText(e.getText());
return e;
}
public String getXMLIdentifier() {
return "text";
}
}

View file

@ -0,0 +1,213 @@
/**
*
* Hilfsklasse zum Umgang mit XML-Dokumenten
* Man hat einen Zeiger, den man durch den XML-Baum bewegen kann.
*
* @version 1.1 vom 14.06.2018
* @author Thomas Schaller
*/
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.io.InputStream;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.xml.sax.InputSource;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;
public class XMLDokument {
// Anfang Attribute
private Document doc;
private ArrayList<Element> pointer;
// Ende Attribute
/** Leeres XMLDokument erzeugen */
public XMLDokument() {
this.doc = new Document();
this.pointer = new ArrayList<Element>();
this.pointer.add(null);
}
/** XML-Dokument aus Datei erzeugen */
public XMLDokument(String dateiname) {
oeffneDokument(dateiname);
}
// Anfang Methoden
/** Öffnet das durch den Dateinamen gegebene Dokument
* @param dateiname Dateiname des XML-Files
*/
public void oeffneDokument(String dateiname) {
doc = null;
File f = new File(dateiname);
try {
// Das Dokument erstellen
SAXBuilder builder = new SAXBuilder();
InputSource is = new InputSource(new FileInputStream(f));
is.setEncoding("UTF-8");
doc = builder.build(is);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
pointer = new ArrayList<Element>(); // Zeiger im Baum auf Root-Element
pointer.add(doc.getRootElement());
}
/** Speichert den XML-Baum im angegebenen Dateinamen
* @param dateiname Dateiname des XML-Files
*/
public void speichereDokument(String dateiname) {
try {
// new XMLOutputter().output(doc, System.out);
XMLOutputter xmlOutput = new XMLOutputter();
// display nice nice
xmlOutput.setFormat(Format.getPrettyFormat());
File f = new File(dateiname);
//FileOutputStream outputFile = new FileOutputStream(f);
PrintWriter outputFile = new PrintWriter(f, "UTF-8");
System.out.println("Speicher in : "+f.getAbsolutePath() );
xmlOutput.output(doc, outputFile);
outputFile.close();
System.out.println("File Saved!");
} catch (IOException io) {
System.out.println(io.getMessage());
}
}
//----------------------------------------------- Zeigerbewegungen --------------------------------------------------
/** Bewegt den Zeiger in der gleiche Ebene zum nächsten Eintrag (falls es dieses gibt)
* @return gibt es noch ein weiteres Element?
*/
public boolean step() {
if(pointer.size() > 1 && pointer.get(0) != null) { // ist das XML-Dokument nicht komplett leer
Element e = pointer.get(1);
Element e2 = pointer.get(0);
List<Element> l = e.getChildren(); // alle Kinder des übergeordneten Elements
if (l !=null) {
int i = l.indexOf(e2);
if (i+1 < l.size()) { // gibt es noch ein nächstes Kind?
pointer.remove(0);
pointer.add(0,l.get(i+1));
return true;
}
}
}
return false;
}
/** Wechselt eine Ebene tiefer im XML-Baum, d.h. zu den Kindern des aktuellen Elements (falls es diese gibt, sonst wird null-Pointer als aktuelles Element gesetzt)
* @return false, wenn es gar keine Kinder gibt, sonst true.
*/
public boolean stepInto() {
if(pointer.size() > 0 && pointer.get(0)!=null) { // ist XML-Baum nicht komplett leer
Element e = pointer.get(0);
List<Element> l = e.getChildren(); // bestimme Kinder des aktuellen Elements
if (l != null && l.size() > 0) { // tauche ein, wenn es welche gibt
pointer.add(0,l.get(0));
return true;
} else {
pointer.add(0,null);
} // end of if-else
}
return false;
}
/** Kehre eine Ebene zurück
* @return true, wenn dies möglich ist, false, wenn schon auf höchster Ebenen
*/
public boolean stepOut() {
if(pointer.size() > 1) {
pointer.remove(0);
return true;
}
return false;
}
//--------------------------------------------------- Methoden für das aktuelle Element -------------------------------------------------
/** Frage den Namen des aktuellen Elements ab*/
public String getName() {
if (pointer.size() == 0 || pointer.get(0)==null) return "";
return pointer.get(0).getName();
}
/** Setze den Namen des aktuellen Elements */
public void setName(String name) {
if (pointer.size() == 0 || pointer.get(0)==null) return;
pointer.get(0).setName(name);
}
/** Frage einen Attributwert des aktuellen Elements ab*/
public String getAttributwert(String attribut) {
if (pointer.size() == 0 || pointer.get(0)==null) return "";
return pointer.get(0).getAttributeValue(attribut);
}
/** Setze einen Attributwert des aktuellen Elements */
public void setAttributwert(String attribut, String wert) {
if (pointer.size() == 0 || pointer.get(0)==null) return;
pointer.get(0).setAttribute(attribut, wert);
}
/** Frage den Inhalt/Text des aktuellen Elements ab*/
public String getInhalt() {
if (pointer.size() == 0 || pointer.get(0)==null) return "";
return pointer.get(0).getValue();
}
/** Setze den Inhalt/Text des aktuellen Elements */
public void setInhalt(String inhalt) {
if (pointer.size() == 0 || pointer.get(0)==null) return;
pointer.get(0).setText(inhalt);
}
// ----------------------------------------------- XML-Struktur aufbauen ------------------------------------------------
/** Erzeuge neues Element nach der aktuellen Position und setze dieses als aktuelles Element
* @param name Name des neuen Elements
*/
public void fuegeElementHinzu(String name) {
if(pointer.size()==1){ // man ist auf Root-Ebene
if(pointer.get(0)==null) { // XML-Baum ist bisher leer => neues Root-Element , sonst wird Element ignoriert, es gibt nur ein Root-Element
Element e = new Element(name);
doc.setRootElement(e);
pointer.remove(0);
pointer.add(0,e);
}
} else {
Element parent = pointer.get(1); // Neues Element wird nach aktuellem eingefügt
Element aktuell = pointer.get(0);
Element e = new Element(name);
if(aktuell == null) {
parent.addContent(e);
pointer.remove(0);
pointer.add(0,e);
} else {
parent.addContent(parent.indexOf(aktuell)+1, e);
pointer.remove(0);
pointer.add(0,e);
} // end of if-else
} // end of if-else
}
}

View file

@ -0,0 +1,36 @@
import java.awt.Color;
/**
*
* Hilfsklasse für ein Ziffer-Element (varibaler Ziffer)
*
*
* @version 1.0 vom 13.04.2018
* @author Thomas Schaller
*/
public class ZifferElement extends Element {
public ZifferElement() {
super();
super.setText("#");
}
public void setText(String t) {
super.setText("#");
}
public Color getColor() {
return Color.blue;
}
public Element getCopy() {
ZifferElement e = new ZifferElement();
return e;
}
public String getXMLIdentifier() {
return "ziffer";
}
}

View file

@ -0,0 +1,11 @@
= Material der ZPG IMP 8:
|===
|Zuordnung| Rechner und Netze
|Klassenstufe| IMP 8
|Bildungsplanbezug |
|Werkzeug|
|Autoren|
|===
== Inhalt