diff --git a/Software/run_n_imp8_kommunikationsprotokoll/.gitignore b/Software/run_n_imp8_kommunikationsprotokoll/.gitignore new file mode 100644 index 0000000..c1f4551 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/.gitignore @@ -0,0 +1,5 @@ +*.class +*.ctxt +*.sh +repo.adoc +*.~lock diff --git a/Software/run_n_imp8_kommunikationsprotokoll/Base64.java b/Software/run_n_imp8_kommunikationsprotokoll/Base64.java new file mode 100644 index 0000000..bb85f31 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/Base64.java @@ -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(); + } +} diff --git a/Software/run_n_imp8_kommunikationsprotokoll/BuchstabeElement.java b/Software/run_n_imp8_kommunikationsprotokoll/BuchstabeElement.java new file mode 100644 index 0000000..844f65e --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/BuchstabeElement.java @@ -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"; + } +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.jar b/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.jar new file mode 100644 index 0000000..82c025f Binary files /dev/null and b/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.jar differ diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.java b/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.java new file mode 100644 index 0000000..f94da44 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/ChatClient.java @@ -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 nachrichten; + protected ArrayList 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 jCBEmpfaenger = new JComboBox(); + private DefaultComboBoxModel jCBEmpfaengerModel = new DefaultComboBoxModel(); + private JPanel jPNachricht = new JPanel(null, true); + private JLabel jLabel7 = new JLabel(); + private JButton jBSend = new JButton(); + private JButton jBProtokollLaden = new JButton(); + private JComboBox jCBLevel = new JComboBox(); + private DefaultComboBoxModel jCBLevelModel = new DefaultComboBoxModel(); + 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("Nachrichten
laut Protokoll"); + 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(); + 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(); + + 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("Nachrichten
laut Protokoll:
"+f.getAttributwert("name")+""); + 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("Von "+n.getAbsender()+""); + 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(""+n.getInhaltString().trim().substring(0,62)+"..."); + } else { + nachricht.setText(""+n.getInhaltString().trim()+""); + } // 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;i0.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 diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.jar b/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.jar new file mode 100644 index 0000000..a50705b Binary files /dev/null and b/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.jar differ diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.java b/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.java new file mode 100644 index 0000000..f3efbe6 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/ChatServerGUI.java @@ -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 clients; + private ArrayList 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(); + names = new ArrayList(); + 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 elemente = new ArrayList(); + 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; + } + + + +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MessageChoosePanel.java b/Software/run_n_imp8_kommunikationsprotokoll/MessageChoosePanel.java new file mode 100644 index 0000000..cd3bee6 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MessageChoosePanel.java @@ -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 ml; + + public MessageChoosePanel() { + this.f = new Color(50,200,50); + this.fo = new Font("SansSerif", Font.BOLD, 12); + this.ml = new ArrayList(); + + } + + 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); + } + + +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MessagePanel.java b/Software/run_n_imp8_kommunikationsprotokoll/MessagePanel.java new file mode 100644 index 0000000..0aecfd4 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MessagePanel.java @@ -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=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; + } + + +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MessagePopup.java b/Software/run_n_imp8_kommunikationsprotokoll/MessagePopup.java new file mode 100644 index 0000000..9eba591 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MessagePopup.java @@ -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); + } + + + } +} diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MyServer.java b/Software/run_n_imp8_kommunikationsprotokoll/MyServer.java new file mode 100644 index 0000000..0cd3992 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MyServer.java @@ -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 + + diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MyServerSocketListener.java b/Software/run_n_imp8_kommunikationsprotokoll/MyServerSocketListener.java new file mode 100644 index 0000000..bad6d5c --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MyServerSocketListener.java @@ -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. +} diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MySocket.java b/Software/run_n_imp8_kommunikationsprotokoll/MySocket.java new file mode 100644 index 0000000..a8d9309 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MySocket.java @@ -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 +} diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MySocketListener.java b/Software/run_n_imp8_kommunikationsprotokoll/MySocketListener.java new file mode 100644 index 0000000..87097fb --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MySocketListener.java @@ -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. +} + diff --git a/Software/run_n_imp8_kommunikationsprotokoll/MyTextFieldDocument.java b/Software/run_n_imp8_kommunikationsprotokoll/MyTextFieldDocument.java new file mode 100644 index 0000000..fa05d61 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/MyTextFieldDocument.java @@ -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(); + } + } + +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/Nachricht.java b/Software/run_n_imp8_kommunikationsprotokoll/Nachricht.java new file mode 100644 index 0000000..03f448b --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/Nachricht.java @@ -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 +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.jar b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.jar new file mode 100644 index 0000000..3681bc1 Binary files /dev/null and b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.jar differ diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.java b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.java new file mode 100644 index 0000000..6f69e44 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollErstellen.java @@ -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"); + } +} diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ProtokollPopup.java b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollPopup.java new file mode 100644 index 0000000..9d4a011 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/ProtokollPopup.java @@ -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); + } + + + + } + } diff --git a/Software/run_n_imp8_kommunikationsprotokoll/TextBubbleBorder.java b/Software/run_n_imp8_kommunikationsprotokoll/TextBubbleBorder.java new file mode 100644 index 0000000..25b2e51 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/TextBubbleBorder.java @@ -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); + } +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/TextElement.java b/Software/run_n_imp8_kommunikationsprotokoll/TextElement.java new file mode 100644 index 0000000..d522b74 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/TextElement.java @@ -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"; + } + +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/XMLDokument.java b/Software/run_n_imp8_kommunikationsprotokoll/XMLDokument.java new file mode 100644 index 0000000..65b4a65 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/XMLDokument.java @@ -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 pointer; + // Ende Attribute + + /** Leeres XMLDokument erzeugen */ + public XMLDokument() { + this.doc = new Document(); + this.pointer = new ArrayList(); + 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(); // 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 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 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 + } +} + diff --git a/Software/run_n_imp8_kommunikationsprotokoll/ZifferElement.java b/Software/run_n_imp8_kommunikationsprotokoll/ZifferElement.java new file mode 100644 index 0000000..a47cf16 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/ZifferElement.java @@ -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"; + } +} \ No newline at end of file diff --git a/Software/run_n_imp8_kommunikationsprotokoll/readme.adoc b/Software/run_n_imp8_kommunikationsprotokoll/readme.adoc new file mode 100644 index 0000000..808c546 --- /dev/null +++ b/Software/run_n_imp8_kommunikationsprotokoll/readme.adoc @@ -0,0 +1,11 @@ += Material der ZPG IMP 8: + +|=== +|Zuordnung| Rechner und Netze +|Klassenstufe| IMP 8 +|Bildungsplanbezug | +|Werkzeug| +|Autoren| +|=== + +== Inhalt