@@ -0,0 +1,44 @@ | |||
package grafikchat; | |||
import grafikchat.controller.ConnectController; | |||
import grafikchat.controller.GrafikController; | |||
import grafikchat.controller.ReceiveAdapter; | |||
import grafikchat.model.ChatModel; | |||
import grafikchat.view.ChatView; | |||
/** | |||
* Start class, start chat application | |||
* | |||
* @author marian | |||
*/ | |||
public class Start { | |||
public Start() | |||
{ | |||
ChatView view = new ChatView(); | |||
ChatModel model = new ChatModel(); | |||
view.getGvDrawPane().setModel(model); | |||
ConnectController controllerConnect = new ConnectController(model, view); | |||
controllerConnect.registerEvents(); | |||
GrafikController controller = new GrafikController(view, model); | |||
controller.registerEvents(); | |||
ReceiveAdapter rxAdapter = new ReceiveAdapter(view); | |||
model.addObserver(rxAdapter); | |||
view.setTitle("Chat"); | |||
view.setVisible(true); | |||
} | |||
/** | |||
* @param args the command line arguments | |||
*/ | |||
public static void main(String[] args) | |||
{ | |||
Start start = new Start(); | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
package grafikchat.controller; | |||
import grafikchat.model.ChatModel; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
import grafikchat.view.ChatView; | |||
import java.awt.event.ActionEvent; | |||
import java.awt.event.ActionListener; | |||
import java.util.logging.Logger; | |||
import javax.swing.JOptionPane; | |||
/** | |||
* Handle events emitted from connect button | |||
* | |||
* @author marian | |||
*/ | |||
public class ConnectController implements ActionListener | |||
{ | |||
private ChatModel model; | |||
private ChatView view; | |||
private static Logger logger = OhmLogger.getLogger(); | |||
/** | |||
* Constructor, initialize variables | |||
* @param model ChatModel | |||
* @param view ChatView | |||
*/ | |||
public ConnectController(ChatModel model, ChatView view) | |||
{ | |||
this.model = model; | |||
this.view = view; | |||
} | |||
/** | |||
* Register events for connect button and radio button to select between server and client mode. | |||
*/ | |||
public void registerEvents() | |||
{ | |||
this.view.getBtConnect().addActionListener(this); | |||
this.view.getRbClient().addActionListener(this); | |||
this.view.getRbServer().addActionListener(this); | |||
} | |||
/** | |||
* Event handler to take care of registered events | |||
* @param ae occurred event | |||
*/ | |||
@Override | |||
public void actionPerformed(ActionEvent ae) | |||
{ | |||
Object object = ae.getSource(); | |||
if (object == view.getBtConnect()) { | |||
int port = -1; | |||
try { | |||
port = Integer.parseInt(view.getTfPort().getText()); | |||
} catch (NumberFormatException e) { | |||
JOptionPane.showMessageDialog(view, "Port entspricht keiner Zahl!"); | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
if (view.getRbClient().isSelected() && (validIP(view.getTfIP().getText()) == false)) { | |||
JOptionPane.showMessageDialog(view, "Ungültige IP-Adresse!"); | |||
logger.severe("IP-Adresse ungültig"); | |||
return; | |||
} | |||
model.connectToPeer(view.getRbServer().isSelected(), view.getTfIP().getText(), port); | |||
view.getRbClient().setEnabled(false); | |||
view.getRbServer().setEnabled(false); | |||
view.getTfIP().setEnabled(false); | |||
view.getTfPort().setEnabled(false); | |||
} else { | |||
view.getTfIP().setEnabled(view.getRbClient().isSelected()); | |||
} | |||
} | |||
/** | |||
* Primitive check if the ip is valid (only IPv4) | |||
* @param ip IP to check | |||
* @return true if valid, false otherwise | |||
*/ | |||
private boolean validIP (String ip) { | |||
try { | |||
if ( ip == null || ip.isEmpty() ) { | |||
return false; | |||
} | |||
String[] parts = ip.split( "\\." ); | |||
if ( parts.length != 4 ) { | |||
return false; | |||
} | |||
for ( String s : parts ) { | |||
int i = Integer.parseInt( s ); | |||
if ( (i < 0) || (i > 255) ) { | |||
return false; | |||
} | |||
} | |||
if ( ip.endsWith(".") ) { | |||
return false; | |||
} | |||
return true; | |||
} catch (NumberFormatException e) { | |||
logger.severe(e.toString()); | |||
return false; | |||
} | |||
} | |||
} |
@@ -0,0 +1,230 @@ | |||
package grafikchat.controller; | |||
import grafikchat.model.ChatModel; | |||
import grafikchat.view.ChatView; | |||
import java.awt.Point; | |||
import java.awt.event.KeyEvent; | |||
import java.awt.event.KeyListener; | |||
import java.awt.event.MouseEvent; | |||
import java.awt.event.MouseListener; | |||
import java.awt.event.MouseMotionListener; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.ObjectInputStream; | |||
import java.io.ObjectOutputStream; | |||
import java.util.ArrayList; | |||
import java.util.logging.Logger; | |||
import javax.swing.JFileChooser; | |||
import javax.swing.JOptionPane; | |||
import grafikchat.model.Figures; | |||
import grafikchat.model.TransceiverData; | |||
import grafikchat.model.TransceiverDataEvent; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
/** | |||
* Handle events from mouse and keyboard | |||
* | |||
* @author marian | |||
*/ | |||
public class GrafikController implements MouseMotionListener, MouseListener, KeyListener | |||
{ | |||
private final ChatView view; | |||
private final ChatModel model; | |||
private boolean clicked; | |||
private final static Logger logger = OhmLogger.getLogger(); | |||
/** | |||
* Constructor | |||
* @param view UI of zeichenprogramm | |||
* @param model Model of zeichenprogramm | |||
*/ | |||
public GrafikController(ChatView view, ChatModel model) | |||
{ | |||
this.view = view; | |||
this.model = model; | |||
this.clicked = false; | |||
} | |||
/** | |||
* Register events for mouse and keyboard | |||
*/ | |||
public void registerEvents() | |||
{ | |||
view.getGvDrawPane().addMouseMotionListener(this); | |||
view.getGvDrawPane().addMouseListener(this); | |||
view.getGvDrawPane().addKeyListener(this); | |||
view.getGvDrawPane().setFocusable(true); | |||
view.addKeyListener(this); | |||
view.setFocusable(true); | |||
} | |||
@Override | |||
public void mouseDragged(MouseEvent evt) | |||
{ | |||
if (clicked) { | |||
Point p = evt.getPoint(); | |||
model.getIntern().addPoint(p); | |||
model.sendMessage(new TransceiverData(TransceiverDataEvent.NEWPOINT, p)); | |||
view.getGvDrawPane().drawPoint(true); | |||
} | |||
} | |||
@Override | |||
public void mouseMoved(MouseEvent e) {} | |||
@Override | |||
public void mouseClicked(MouseEvent e) {} | |||
@Override | |||
public void mousePressed(MouseEvent e) | |||
{ | |||
if (e.getButton() == MouseEvent.BUTTON1) { | |||
logger.info("Mouse pressed"); | |||
model.getIntern().newFigure(); | |||
model.sendMessage(new TransceiverData(TransceiverDataEvent.NEWFIGURE, null)); | |||
this.clicked = true; | |||
} | |||
} | |||
@Override | |||
public void mouseReleased(MouseEvent e) | |||
{ | |||
if (e.getButton() == MouseEvent.BUTTON1) { | |||
logger.info("Mouse released"); | |||
this.clicked = false; | |||
} | |||
} | |||
@Override | |||
public void mouseEntered(MouseEvent e) {} | |||
@Override | |||
public void mouseExited(MouseEvent e) {} | |||
/** | |||
* Save current graphics to file | |||
* @return True if successful, false otherwise | |||
*/ | |||
public boolean saveToFile() | |||
{ | |||
JFileChooser fc = new JFileChooser(); | |||
fc.setCurrentDirectory(new File(model.getFilename())); | |||
int returnVal = fc.showSaveDialog(view); | |||
if (returnVal == JFileChooser.APPROVE_OPTION) { | |||
File f = fc.getSelectedFile(); | |||
if(!f.exists()) { | |||
try { | |||
f.createNewFile(); | |||
} catch (IOException e) { | |||
JOptionPane.showMessageDialog(view, "Datei konnte nicht erstellt werden.", "Warnung", JOptionPane.WARNING_MESSAGE); | |||
return false; | |||
} | |||
} | |||
} else { | |||
JOptionPane.showMessageDialog(view, "Es wurde keine Datei zum speichern ausgewählt.", "Warnung", JOptionPane.WARNING_MESSAGE); | |||
return false; | |||
} | |||
model.setFilename(fc.getSelectedFile().getPath()); | |||
try { | |||
FileOutputStream fos = new FileOutputStream(model.getFilename()); | |||
try (ObjectOutputStream oos = new ObjectOutputStream(fos)) { | |||
ArrayList<Figures> list = new ArrayList<>(); | |||
list.add(model.getIntern()); | |||
list.add(model.getExtern()); | |||
oos.writeObject(list); | |||
} | |||
} catch (IOException e) { | |||
JOptionPane.showMessageDialog(view, "Grafik konnte nicht gespeichert werden.", "Warning", JOptionPane.WARNING_MESSAGE); | |||
} | |||
JOptionPane.showMessageDialog(view, "Grafik erfolgreich gespeichert.", "Information", JOptionPane.INFORMATION_MESSAGE); | |||
return true; | |||
} | |||
/** | |||
* Read graphics from file | |||
* @return True if successful, false otherwise | |||
*/ | |||
public boolean readFromFile() | |||
{ | |||
JFileChooser fc = new JFileChooser(); | |||
fc.setCurrentDirectory(new File(model.getFilename())); | |||
int returnVal = fc.showOpenDialog(view); | |||
if (returnVal == JFileChooser.APPROVE_OPTION) { | |||
File f = fc.getSelectedFile(); | |||
if(!f.exists()) { | |||
try { | |||
f.createNewFile(); | |||
} catch (IOException e) { | |||
JOptionPane.showMessageDialog(view, "Datei konnte nicht erstellt werden.", "Warnung", JOptionPane.WARNING_MESSAGE); | |||
return false; | |||
} | |||
} | |||
String filepath = f.getPath(); | |||
ArrayList<Figures> d; | |||
FileInputStream is; | |||
try { | |||
is = new FileInputStream(filepath); | |||
} catch (IOException e) { | |||
JOptionPane.showMessageDialog(view, "Datei wurde nicht gefunden.", "Warnung", JOptionPane.WARNING_MESSAGE); | |||
return false; | |||
} | |||
try { | |||
ObjectInputStream ois = new ObjectInputStream(is); | |||
d = (ArrayList<Figures>)ois.readObject(); | |||
model.getIntern().setFigures(d.get(0).getFigures()); | |||
model.getExtern().setFigures(d.get(1).getFigures()); | |||
view.repaint(); | |||
model.sendMessage(new TransceiverData(TransceiverDataEvent.REPAINT, d)); | |||
} catch (ClassNotFoundException | IOException e) { | |||
JOptionPane.showMessageDialog(view, "Leeres Adressbuch geöffnet.", "Warnung", JOptionPane.INFORMATION_MESSAGE); | |||
} | |||
model.setFilename(filepath); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public void keyTyped(KeyEvent ke) | |||
{ | |||
} | |||
@Override | |||
public void keyPressed(KeyEvent ke) | |||
{ | |||
} | |||
@Override | |||
public void keyReleased(KeyEvent ke) | |||
{ | |||
char c = ke.getKeyChar(); | |||
switch (c) | |||
{ | |||
case 's': | |||
saveToFile(); | |||
break; | |||
case 'o': | |||
readFromFile(); | |||
break; | |||
case 'p': | |||
view.getGvDrawPane().doPrint(); | |||
break; | |||
} | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package grafikchat.controller; | |||
import grafikchat.model.ChatModel; | |||
import grafikchat.model.TransceiverDataEvent; | |||
import grafikchat.view.ChatView; | |||
import java.util.Observable; | |||
import java.util.Observer; | |||
/** | |||
* Take care of received messages. Show to user in UI | |||
* | |||
* @author marian | |||
*/ | |||
public class ReceiveAdapter implements Observer | |||
{ | |||
private ChatView view; | |||
/** | |||
* Constructor, get reference to view | |||
* @param view ChatView | |||
*/ | |||
public ReceiveAdapter(ChatView view) | |||
{ | |||
this.view = view; | |||
} | |||
/** | |||
* Incoming information from Observable | |||
* @param obs Observable | |||
* @param object Received message | |||
*/ | |||
@Override | |||
public void update(Observable obs, Object object) | |||
{ | |||
TransceiverDataEvent mode = (TransceiverDataEvent)object; | |||
switch (mode) | |||
{ | |||
case NEWPOINT: | |||
view.getGvDrawPane().drawPoint(false); | |||
break; | |||
case REPAINT: | |||
view.getGvDrawPane().repaint(); | |||
break; | |||
} | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
package grafikchat.model; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
import java.awt.Point; | |||
import java.util.ArrayList; | |||
import java.util.Observable; | |||
import java.util.Observer; | |||
import java.util.logging.Logger; | |||
import java.util.prefs.Preferences; | |||
/** | |||
* Model of chat application. Handle communication | |||
* | |||
* @author marian | |||
*/ | |||
public class ChatModel extends Observable implements Observer | |||
{ | |||
private boolean connected; | |||
private boolean mode; | |||
private boolean initialized; | |||
private Transceiver trx; | |||
private Figures intern; | |||
private Figures extern; | |||
private final Preferences prefs; | |||
private static Logger logger = OhmLogger.getLogger(); | |||
/** | |||
* Constructor, initialize variables | |||
*/ | |||
public ChatModel() | |||
{ | |||
connected = false; | |||
mode = false; | |||
initialized = false; | |||
trx = null; | |||
intern = new Figures(); | |||
extern = new Figures(); | |||
prefs = Preferences.userNodeForPackage(this.getClass()); | |||
} | |||
/** | |||
* Get local drawn figure | |||
* @return internal figure | |||
*/ | |||
public Figures getIntern() | |||
{ | |||
return intern; | |||
} | |||
/** | |||
* Get remote drawn figure | |||
* @return extern figure | |||
*/ | |||
public Figures getExtern() | |||
{ | |||
return extern; | |||
} | |||
/** | |||
* Set new filename in preferences | |||
* @param f | |||
*/ | |||
public void setFilename(String f) | |||
{ | |||
prefs.put("LAST_FILE", f); | |||
} | |||
/** | |||
* Get latest filename from preferences | |||
* @return filename | |||
*/ | |||
public String getFilename() | |||
{ | |||
String last = prefs.get("LAST_FILE", ""); | |||
System.out.println(last); | |||
return last; | |||
} | |||
/** | |||
* Connect to peer | |||
* @param mode true to use server mode, false to use client mode | |||
* @param ip IP to connect to (not relevant in server mode) | |||
* @param port Port to open or connect to (depending on selected mode) | |||
*/ | |||
public void connectToPeer(boolean mode, String ip, int port) | |||
{ | |||
if (initialized) { | |||
logger.info("Chat already running"); | |||
return; | |||
} | |||
this.mode = mode; | |||
if (mode) { | |||
logger.info("Running in server mode"); | |||
trx = new Server(port); | |||
} else { | |||
logger.info("Running in client mode"); | |||
trx = new Client(ip, port); | |||
} | |||
trx.registerObserver(this); | |||
trx.init(); | |||
initialized = true; | |||
} | |||
/** | |||
* Send message to peer | |||
* @param d TransceiverData to transmit | |||
*/ | |||
public void sendMessage(TransceiverData d) | |||
{ | |||
if (!initialized) { | |||
logger.warning("Chat not initialized"); | |||
return; | |||
} | |||
trx.sendMessage(d); | |||
} | |||
/** | |||
* Get received data from network | |||
* @param obs Observable | |||
* @param object Received object | |||
*/ | |||
@Override | |||
public void update(Observable obs, Object object) | |||
{ | |||
TransceiverData d = (TransceiverData)object; | |||
if (object instanceof TransceiverData) { | |||
switch (d.getEvent()) | |||
{ | |||
case NEWFIGURE: | |||
extern.newFigure(); | |||
break; | |||
case NEWPOINT: | |||
extern.addPoint((Point)d.getObject()); | |||
this.setChanged(); | |||
this.notifyObservers(TransceiverDataEvent.NEWPOINT); | |||
break; | |||
case REPAINT: | |||
ArrayList<Figures> fig = (ArrayList<Figures>)d.getObject(); | |||
intern.setFigures(fig.get(0).getFigures()); | |||
extern.setFigures(fig.get(1).getFigures()); | |||
this.setChanged(); | |||
this.notifyObservers(TransceiverDataEvent.REPAINT); | |||
default: | |||
logger.warning("Unknown TransceiverDataEvent"); | |||
} | |||
} else { | |||
logger.warning("Received invalid object"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,148 @@ | |||
package grafikchat.model; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
import java.io.IOException; | |||
import java.io.ObjectInputStream; | |||
import java.io.ObjectOutputStream; | |||
import java.net.Socket; | |||
import java.util.Observable; | |||
import java.util.Observer; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
/** | |||
* TCP Client | |||
* | |||
* @author marian | |||
*/ | |||
public class Client extends Observable implements Runnable, Transceiver | |||
{ | |||
private Socket socket; | |||
private ObjectInputStream reader; | |||
private ObjectOutputStream writer; | |||
private boolean ready; | |||
private Thread thd; | |||
private String ip; | |||
private int port; | |||
private static Logger logger = OhmLogger.getLogger(); | |||
/** | |||
* Constructor, initialize variables | |||
* @param ip IP to connect to | |||
* @param port Port of remote server | |||
*/ | |||
public Client(String ip, int port) | |||
{ | |||
this.ip = ip; | |||
this.port = port; | |||
this.socket = null; | |||
this.reader = null; | |||
this.writer = null; | |||
this.ready = false; | |||
this.thd = null; | |||
} | |||
/** | |||
* Init client thread, if not already done | |||
*/ | |||
@Override | |||
public void init() | |||
{ | |||
if (thd == null) { | |||
thd = new Thread(this); | |||
thd.start(); | |||
} | |||
} | |||
/** | |||
* Add observer to client | |||
* @param o Observer to add | |||
*/ | |||
@Override | |||
public void registerObserver(Observer o) | |||
{ | |||
this.addObserver(o); | |||
} | |||
/** | |||
* Run the client in thread. | |||
*/ | |||
@Override | |||
public void run() | |||
{ | |||
logger.info("Running client..."); | |||
while (true) | |||
{ | |||
if (socket == null) { | |||
try { | |||
socket = new Socket(ip, port); | |||
logger.info("Connected to server"); | |||
} catch (IOException e) { | |||
logger.severe(e.getMessage()); | |||
socket = null; | |||
try { | |||
Thread.sleep(1000); | |||
} catch (InterruptedException ex) { | |||
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex); | |||
} | |||
continue; | |||
} | |||
try { | |||
reader = new ObjectInputStream(socket.getInputStream()); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
try { | |||
writer = new ObjectOutputStream(socket.getOutputStream()); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
ready = true; | |||
} | |||
if (ready) { | |||
try { | |||
//logger.info("Waiting for message"); | |||
Object o = reader.readObject(); | |||
if (o == null) { | |||
logger.warning("Reached end of stream"); | |||
ready = false; | |||
continue; | |||
} | |||
this.setChanged(); | |||
this.notifyObservers(o); | |||
} catch (ClassNotFoundException | IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Send message to server | |||
* @param d TransceiverData to transmit | |||
*/ | |||
@Override | |||
public void sendMessage(TransceiverData d) | |||
{ | |||
if (ready) { | |||
try { | |||
writer.writeObject(d); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
} | |||
} else { | |||
logger.warning("Server not ready to send message. Connect first"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package grafikchat.model; | |||
import java.awt.Point; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* Figure drawn by user with mouse | |||
* | |||
* @author marian | |||
*/ | |||
public class Figure implements Serializable | |||
{ | |||
private static final long serialVersionUID = 8582433437601788991L; | |||
private final ArrayList<Point> points; | |||
/** | |||
* Constructor, initialize figure | |||
*/ | |||
public Figure() | |||
{ | |||
points = new ArrayList<>(); | |||
} | |||
/** | |||
* Add point to figure | |||
* @param p new point | |||
*/ | |||
public void addPoint(Point p) | |||
{ | |||
points.add(p); | |||
} | |||
/** | |||
* Get all points of figure | |||
* @return all points | |||
*/ | |||
public List<Point> getPoints() | |||
{ | |||
return points; | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
package grafikchat.model; | |||
import java.awt.Point; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* Implementation of a list of figures | |||
* | |||
* @author marian | |||
*/ | |||
public class Figures implements Serializable | |||
{ | |||
private ArrayList<Figure> figureList; | |||
private Figure lastFigure; | |||
private Point lastPoint; | |||
private Point secondToLastPoint; | |||
private static final long serialVersionUID = 8582433437601788990L; | |||
/** | |||
* Constructor, initialize variables | |||
*/ | |||
public Figures() | |||
{ | |||
figureList = new ArrayList<>(); | |||
lastFigure = null; | |||
lastPoint = null; | |||
secondToLastPoint = null; | |||
} | |||
/** | |||
* Add point to latest figure | |||
* @param p new point to add | |||
*/ | |||
public void addPoint(Point p) | |||
{ | |||
lastFigure.addPoint(p); | |||
secondToLastPoint = lastPoint; | |||
lastPoint = p; | |||
} | |||
/** | |||
* Create new figure | |||
*/ | |||
public void newFigure() | |||
{ | |||
Figure f = new Figure(); | |||
figureList.add(f); | |||
lastFigure = f; | |||
lastPoint = null; | |||
secondToLastPoint = null; | |||
} | |||
/** | |||
* Get the last two points of the latest figure | |||
* @return last two points of latest figure | |||
* @throws Exception if not yet available | |||
*/ | |||
public List<Point> getLastTwoPoints() throws Exception | |||
{ | |||
if (secondToLastPoint == null || lastPoint == null) { | |||
throw new Exception("Not enough points available"); | |||
} | |||
ArrayList<Point> p = new ArrayList<>(); | |||
p.add(lastPoint); | |||
p.add(secondToLastPoint); | |||
return p; | |||
} | |||
/** | |||
* Get latest figure | |||
* @return latest figure | |||
* @throws Exception if not yet available | |||
*/ | |||
private List<Point> getFigure() throws Exception | |||
{ | |||
if (lastFigure == null) { | |||
throw new Exception("No figure available"); | |||
} | |||
return lastFigure.getPoints(); | |||
} | |||
/** | |||
* Set new figures | |||
* @param f | |||
*/ | |||
public void setFigures(ArrayList<Figure> f) | |||
{ | |||
figureList = f; | |||
} | |||
/** | |||
* Get all current figures | |||
* @return | |||
*/ | |||
public ArrayList<Figure> getFigures() | |||
{ | |||
return figureList; | |||
} | |||
} |
@@ -0,0 +1,151 @@ | |||
package grafikchat.model; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
import java.io.IOException; | |||
import java.io.ObjectInputStream; | |||
import java.io.ObjectOutputStream; | |||
import java.net.ServerSocket; | |||
import java.net.Socket; | |||
import java.util.Observable; | |||
import java.util.Observer; | |||
import java.util.logging.Logger; | |||
/** | |||
* TCP Server | |||
* | |||
* @author marian | |||
*/ | |||
public class Server extends Observable implements Runnable, Transceiver | |||
{ | |||
private int port; | |||
private ServerSocket server; | |||
private Socket client; | |||
private static Logger logger = OhmLogger.getLogger(); | |||
private ObjectInputStream reader; | |||
private ObjectOutputStream writer; | |||
private volatile boolean ready; | |||
private Thread thd; | |||
/** | |||
* Constructor, initialize variables | |||
* @param port Port to listen on | |||
*/ | |||
public Server(int port) | |||
{ | |||
this.port = port; | |||
this.server = null; | |||
this.client = null; | |||
this.reader = null; | |||
this.writer = null; | |||
this.ready = false; | |||
this.thd = null; | |||
} | |||
/** | |||
* Init server if not already done | |||
*/ | |||
@Override | |||
public void init() | |||
{ | |||
if (thd == null) { | |||
thd = new Thread(this); | |||
thd.start(); | |||
} | |||
} | |||
/** | |||
* Add observer to server | |||
* @param o Observer to add | |||
*/ | |||
@Override | |||
public void registerObserver(Observer o) | |||
{ | |||
this.addObserver(o); | |||
} | |||
/** | |||
* Run server in thread | |||
*/ | |||
@Override | |||
public void run() | |||
{ | |||
logger.info("Running server..."); | |||
while (true) | |||
{ | |||
if (server == null) { | |||
try { | |||
server = new ServerSocket(port); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
logger.info("Waiting for client to connect"); | |||
try { | |||
client = server.accept(); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
logger.info("Client connected"); | |||
try { | |||
writer = new ObjectOutputStream(client.getOutputStream()); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
try { | |||
reader = new ObjectInputStream(client.getInputStream()); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
ready = true; | |||
} | |||
if (ready) { | |||
try { | |||
//logger.info("Waiting for message"); | |||
Object o = reader.readObject(); | |||
if (o == null) { | |||
logger.warning("Reached end of stream"); | |||
ready = false; | |||
continue; | |||
} | |||
this.setChanged(); | |||
this.notifyObservers(o); | |||
} catch (ClassNotFoundException | IOException e) { | |||
logger.severe(e.toString()); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Send message to client | |||
* @param d TransceiverData to transmit | |||
*/ | |||
@Override | |||
public void sendMessage(TransceiverData d) | |||
{ | |||
if (ready) { | |||
try { | |||
writer.writeObject(d); | |||
} catch (IOException e) { | |||
logger.severe(e.toString()); | |||
} | |||
} else { | |||
logger.warning("Server not ready to send message. Connect first"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
package grafikchat.model; | |||
import java.util.Observer; | |||
/** | |||
* Interface for server and client | |||
* | |||
* @author marian | |||
*/ | |||
public interface Transceiver | |||
{ | |||
/** | |||
* Initialize the transceiver | |||
*/ | |||
public void init(); | |||
/** | |||
* Send object | |||
* @param d TransceiverData to send | |||
*/ | |||
public void sendMessage(TransceiverData d); | |||
/** | |||
* Register on transceiver | |||
* @param o Object to register | |||
*/ | |||
public void registerObserver(Observer o); | |||
} |
@@ -0,0 +1,31 @@ | |||
package grafikchat.model; | |||
import java.io.Serializable; | |||
/** | |||
* DataPoint for communication between two sockets | |||
* | |||
* @author marian | |||
*/ | |||
public class TransceiverData implements Serializable | |||
{ | |||
private final TransceiverDataEvent e; | |||
private final Object o; | |||
private static final long serialVersionUID = 8582433437601788989L; | |||
public TransceiverData(TransceiverDataEvent e, Object o) | |||
{ | |||
this.e = e; | |||
this.o = o; | |||
} | |||
public TransceiverDataEvent getEvent() | |||
{ | |||
return this.e; | |||
} | |||
public Object getObject() | |||
{ | |||
return this.o; | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
package grafikchat.model; | |||
public enum TransceiverDataEvent | |||
{ | |||
NEWPOINT, | |||
NEWFIGURE, | |||
REPAINT; | |||
} |
@@ -0,0 +1,34 @@ | |||
package grafikchat.ohmlogger; | |||
import java.text.DateFormat; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
import java.util.logging.*; | |||
/** | |||
* Formatter with custom format | |||
* | |||
* @author marian | |||
*/ | |||
class OhmFormatter extends SimpleFormatter { | |||
private static final DateFormat df = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss.SSS"); | |||
/** | |||
* Format message to specified format | |||
* @param record Tecord to format | |||
* @return Formatted string | |||
*/ | |||
@Override | |||
public String format(LogRecord record) { | |||
StringBuilder builder; | |||
builder = new StringBuilder(1000); | |||
builder.append(df.format(new Date(record.getMillis()))).append(" | "); | |||
builder.append(record.getLevel()).append(" | "); | |||
builder.append(record.getSourceClassName()).append(" | "); | |||
builder.append(record.getSourceMethodName()).append(" | "); | |||
builder.append(formatMessage(record)).append(" |"); | |||
builder.append("\n"); | |||
return builder.toString(); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
package grafikchat.ohmlogger; | |||
import java.util.logging.ConsoleHandler; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
/** | |||
* Logger with custom format | |||
* | |||
* @author marian | |||
*/ | |||
public class OhmLogger | |||
{ | |||
private static Logger lg; | |||
/** | |||
* Get logger object | |||
* | |||
* @return logger object | |||
*/ | |||
public static Logger getLogger() | |||
{ | |||
if (lg == null) { | |||
lg = Logger.getLogger("OhmLogger"); | |||
initLogger(); | |||
} | |||
return lg; | |||
} | |||
/** | |||
* Initialize new logger and set custom formatter | |||
*/ | |||
private static void initLogger() | |||
{ | |||
ConsoleHandler ch = new ConsoleHandler(); | |||
ch.setFormatter(new OhmFormatter()); | |||
lg.addHandler(ch); | |||
lg.setUseParentHandlers(false); | |||
lg.setLevel(Level.ALL); | |||
} | |||
} |
@@ -0,0 +1,138 @@ | |||
<?xml version="1.0" encoding="UTF-8" ?> | |||
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JFrameFormInfo"> | |||
<NonVisualComponents> | |||
<Component class="javax.swing.ButtonGroup" name="bgSelect"> | |||
</Component> | |||
</NonVisualComponents> | |||
<Properties> | |||
<Property name="defaultCloseOperation" type="int" value="3"/> | |||
</Properties> | |||
<SyntheticProperties> | |||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/> | |||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/> | |||
</SyntheticProperties> | |||
<AuxValues> | |||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> | |||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> | |||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> | |||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> | |||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> | |||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> | |||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> | |||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> | |||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> | |||
</AuxValues> | |||
<Layout> | |||
<DimensionLayout dim="0"> | |||
<Group type="103" groupAlignment="0" attributes="0"> | |||
<Group type="102" alignment="0" attributes="0"> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Group type="103" groupAlignment="0" attributes="0"> | |||
<Component id="gvDrawPane" max="32767" attributes="0"/> | |||
<Group type="102" alignment="0" attributes="0"> | |||
<Component id="rbClient" max="32767" attributes="0"/> | |||
<EmptySpace min="-2" pref="215" max="-2" attributes="0"/> | |||
</Group> | |||
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/> | |||
<Group type="102" alignment="0" attributes="0"> | |||
<Component id="lbPort" min="-2" max="-2" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="tfPort" min="-2" pref="61" max="-2" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="lbIP" min="-2" max="-2" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="tfIP" pref="164" max="32767" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="btConnect" min="-2" max="-2" attributes="0"/> | |||
</Group> | |||
<Group type="102" attributes="0"> | |||
<Component id="rbServer" min="-2" max="-2" attributes="0"/> | |||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> | |||
</Group> | |||
</Group> | |||
<EmptySpace max="-2" attributes="0"/> | |||
</Group> | |||
</Group> | |||
</DimensionLayout> | |||
<DimensionLayout dim="1"> | |||
<Group type="103" groupAlignment="0" attributes="0"> | |||
<Group type="102" alignment="0" attributes="0"> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="rbClient" min="-2" max="-2" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="rbServer" min="-2" max="-2" attributes="0"/> | |||
<EmptySpace type="unrelated" max="-2" attributes="0"/> | |||
<Group type="103" groupAlignment="3" attributes="0"> | |||
<Component id="lbPort" alignment="3" min="-2" max="-2" attributes="0"/> | |||
<Component id="tfPort" alignment="3" min="-2" max="-2" attributes="0"/> | |||
<Component id="tfIP" alignment="3" min="-2" max="-2" attributes="0"/> | |||
<Component id="lbIP" alignment="3" min="-2" max="-2" attributes="0"/> | |||
<Component id="btConnect" alignment="3" min="-2" max="-2" attributes="0"/> | |||
</Group> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
<Component id="gvDrawPane" pref="205" max="32767" attributes="0"/> | |||
<EmptySpace max="-2" attributes="0"/> | |||
</Group> | |||
</Group> | |||
</DimensionLayout> | |||
</Layout> | |||
<SubComponents> | |||
<Component class="javax.swing.JTextField" name="tfPort"> | |||
<Properties> | |||
<Property name="text" type="java.lang.String" value="3210"/> | |||
<Property name="toolTipText" type="java.lang.String" value=""/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JLabel" name="lbPort"> | |||
<Properties> | |||
<Property name="text" type="java.lang.String" value="Port:"/> | |||
<Property name="toolTipText" type="java.lang.String" value=""/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JLabel" name="lbIP"> | |||
<Properties> | |||
<Property name="text" type="java.lang.String" value="IP:"/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JTextField" name="tfIP"> | |||
<Properties> | |||
<Property name="text" type="java.lang.String" value="127.0.0.1"/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JButton" name="btConnect"> | |||
<Properties> | |||
<Property name="text" type="java.lang.String" value="connect"/> | |||
<Property name="toolTipText" type="java.lang.String" value=""/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JSeparator" name="jSeparator1"> | |||
</Component> | |||
<Component class="javax.swing.JRadioButton" name="rbClient"> | |||
<Properties> | |||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> | |||
<ComponentRef name="bgSelect"/> | |||
</Property> | |||
<Property name="selected" type="boolean" value="true"/> | |||
<Property name="text" type="java.lang.String" value="Client"/> | |||
</Properties> | |||
</Component> | |||
<Component class="javax.swing.JRadioButton" name="rbServer"> | |||
<Properties> | |||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> | |||
<ComponentRef name="bgSelect"/> | |||
</Property> | |||
<Property name="text" type="java.lang.String" value="Server"/> | |||
</Properties> | |||
</Component> | |||
<Container class="grafikchat.view.GrafikView" name="gvDrawPane"> | |||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout"> | |||
<Property name="useNullLayout" type="boolean" value="true"/> | |||
</Layout> | |||
</Container> | |||
</SubComponents> | |||
</Form> |
@@ -0,0 +1,218 @@ | |||
/* | |||
* To change this license header, choose License Headers in Project Properties. | |||
* To change this template file, choose Tools | Templates | |||
* and open the template in the editor. | |||
*/ | |||
package grafikchat.view; | |||
/** | |||
* | |||
* @author marian | |||
*/ | |||
public class ChatView extends javax.swing.JFrame { | |||
/** | |||
* @return the rbClient | |||
*/ | |||
public javax.swing.JRadioButton getRbClient() { | |||
return rbClient; | |||
} | |||
/** | |||
* @return the rbServer | |||
*/ | |||
public javax.swing.JRadioButton getRbServer() { | |||
return rbServer; | |||
} | |||
/** | |||
* @return the btConnect | |||
*/ | |||
public javax.swing.JButton getBtConnect() { | |||
return btConnect; | |||
} | |||
/** | |||
* @return the lbIP | |||
*/ | |||
public javax.swing.JLabel getLbIP() { | |||
return lbIP; | |||
} | |||
/** | |||
* @return the lbPort | |||
*/ | |||
public javax.swing.JLabel getLbPort() { | |||
return lbPort; | |||
} | |||
/** | |||
* @return the tfIP | |||
*/ | |||
public javax.swing.JTextField getTfIP() { | |||
return tfIP; | |||
} | |||
/** | |||
* @return the tfPort | |||
*/ | |||
public javax.swing.JTextField getTfPort() { | |||
return tfPort; | |||
} | |||
/** | |||
* Creates new form ChatView | |||
*/ | |||
public ChatView() { | |||
initComponents(); | |||
} | |||
/** | |||
* This method is called from within the constructor to initialize the form. | |||
* WARNING: Do NOT modify this code. The content of this method is always | |||
* regenerated by the Form Editor. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents | |||
private void initComponents() { | |||
bgSelect = new javax.swing.ButtonGroup(); | |||
tfPort = new javax.swing.JTextField(); | |||
lbPort = new javax.swing.JLabel(); | |||
lbIP = new javax.swing.JLabel(); | |||
tfIP = new javax.swing.JTextField(); | |||
btConnect = new javax.swing.JButton(); | |||
jSeparator1 = new javax.swing.JSeparator(); | |||
rbClient = new javax.swing.JRadioButton(); | |||
rbServer = new javax.swing.JRadioButton(); | |||
gvDrawPane = new grafikchat.view.GrafikView(); | |||
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); | |||
tfPort.setText("3210"); | |||
tfPort.setToolTipText(""); | |||
lbPort.setText("Port:"); | |||
lbPort.setToolTipText(""); | |||
lbIP.setText("IP:"); | |||
tfIP.setText("127.0.0.1"); | |||
btConnect.setText("connect"); | |||
btConnect.setToolTipText(""); | |||
bgSelect.add(rbClient); | |||
rbClient.setSelected(true); | |||
rbClient.setText("Client"); | |||
bgSelect.add(rbServer); | |||
rbServer.setText("Server"); | |||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); | |||
getContentPane().setLayout(layout); | |||
layout.setHorizontalGroup( | |||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) | |||
.addGroup(layout.createSequentialGroup() | |||
.addContainerGap() | |||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) | |||
.addComponent(gvDrawPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) | |||
.addGroup(layout.createSequentialGroup() | |||
.addComponent(rbClient, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) | |||
.addGap(215, 215, 215)) | |||
.addComponent(jSeparator1) | |||
.addGroup(layout.createSequentialGroup() | |||
.addComponent(lbPort) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(tfPort, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(lbIP) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(tfIP, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(btConnect)) | |||
.addGroup(layout.createSequentialGroup() | |||
.addComponent(rbServer) | |||
.addGap(0, 0, Short.MAX_VALUE))) | |||
.addContainerGap()) | |||
); | |||
layout.setVerticalGroup( | |||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) | |||
.addGroup(layout.createSequentialGroup() | |||
.addContainerGap() | |||
.addComponent(rbClient) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(rbServer) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) | |||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) | |||
.addComponent(lbPort) | |||
.addComponent(tfPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) | |||
.addComponent(tfIP, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) | |||
.addComponent(lbIP) | |||
.addComponent(btConnect)) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE) | |||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) | |||
.addComponent(gvDrawPane, javax.swing.GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE) | |||
.addContainerGap()) | |||
); | |||
pack(); | |||
}// </editor-fold>//GEN-END:initComponents | |||
/** | |||
* @param args the command line arguments | |||
*/ | |||
public static void main(String args[]) { | |||
/* Set the Nimbus look and feel */ | |||
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> | |||
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. | |||
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html | |||
*/ | |||
try { | |||
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { | |||
if ("Nimbus".equals(info.getName())) { | |||
javax.swing.UIManager.setLookAndFeel(info.getClassName()); | |||
break; | |||
} | |||
} | |||
} catch (ClassNotFoundException ex) { | |||
java.util.logging.Logger.getLogger(ChatView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); | |||
} catch (InstantiationException ex) { | |||
java.util.logging.Logger.getLogger(ChatView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); | |||
} catch (IllegalAccessException ex) { | |||
java.util.logging.Logger.getLogger(ChatView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); | |||
} catch (javax.swing.UnsupportedLookAndFeelException ex) { | |||
java.util.logging.Logger.getLogger(ChatView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); | |||
} | |||
//</editor-fold> | |||
/* Create and display the form */ | |||
java.awt.EventQueue.invokeLater(new Runnable() { | |||
public void run() { | |||
new ChatView().setVisible(true); | |||
} | |||
}); | |||
} | |||
// Variables declaration - do not modify//GEN-BEGIN:variables | |||
private javax.swing.ButtonGroup bgSelect; | |||
private javax.swing.JButton btConnect; | |||
private grafikchat.view.GrafikView gvDrawPane; | |||
private javax.swing.JSeparator jSeparator1; | |||
private javax.swing.JLabel lbIP; | |||
private javax.swing.JLabel lbPort; | |||
private javax.swing.JRadioButton rbClient; | |||
private javax.swing.JRadioButton rbServer; | |||
private javax.swing.JTextField tfIP; | |||
private javax.swing.JTextField tfPort; | |||
// End of variables declaration//GEN-END:variables | |||
/** | |||
* @return the gvDrawPane | |||
*/ | |||
public grafikchat.view.GrafikView getGvDrawPane() { | |||
return gvDrawPane; | |||
} | |||
} |
@@ -0,0 +1,184 @@ | |||
package grafikchat.view; | |||
import java.awt.BasicStroke; | |||
import java.awt.Color; | |||
import java.awt.Graphics; | |||
import java.awt.Graphics2D; | |||
import java.awt.Point; | |||
import java.awt.RenderingHints; | |||
import java.awt.geom.Line2D; | |||
import java.awt.print.PageFormat; | |||
import java.awt.print.Printable; | |||
import java.awt.print.PrinterException; | |||
import java.awt.print.PrinterJob; | |||
import java.util.List; | |||
import java.util.logging.Logger; | |||
import javax.print.attribute.HashPrintRequestAttributeSet; | |||
import javax.print.attribute.standard.DialogTypeSelection; | |||
import javax.swing.JComponent; | |||
import javax.swing.JOptionPane; | |||
import grafikchat.model.Figure; | |||
import grafikchat.model.ChatModel; | |||
import grafikchat.model.Figures; | |||
import grafikchat.ohmlogger.OhmLogger; | |||
/** | |||
* View of zeichenprogramm | |||
* | |||
* @author marian | |||
*/ | |||
public class GrafikView extends JComponent implements Printable | |||
{ | |||
private final static Logger lg = OhmLogger.getLogger(); | |||
private Line2D.Double line; | |||
private ChatModel model; | |||
private final static Color colorInternal = Color.BLACK; | |||
private final static Color colorExternal = Color.RED; | |||
/** | |||
* Constructor, initialize UI | |||
*/ | |||
public GrafikView() | |||
{ | |||
this.model = null; | |||
this.setBackground(Color.WHITE); | |||
line = new Line2D.Double(); | |||
lg.info("Call constructor of grafikview"); | |||
} | |||
/** | |||
* Set model | |||
* @param model model of MVC | |||
*/ | |||
public void setModel(ChatModel model) | |||
{ | |||
this.model = model; | |||
lg.info("Set model of grafik view"); | |||
} | |||
/** | |||
* Draw point (line with the point before) | |||
* @param mode True to draw internal point, false to draw external point | |||
*/ | |||
public void drawPoint(boolean mode) | |||
{ | |||
if (this.model == null) { | |||
lg.warning("Model not initialized"); | |||
return; | |||
} | |||
Graphics2D g2 = (Graphics2D) this.getGraphics(); | |||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |||
if (mode) { | |||
drawLine(model.getIntern(), colorInternal, g2); | |||
} else { | |||
drawLine(model.getExtern(), colorExternal, g2); | |||
} | |||
g2.dispose(); | |||
} | |||
/** | |||
* Draw new line between two points | |||
* @param f Figures with points | |||
* @param c Color of line | |||
* @param g2 Graphics context | |||
*/ | |||
private void drawLine(Figures f, Color c, Graphics2D g2) | |||
{ | |||
List<Point> points; | |||
try { | |||
points = f.getLastTwoPoints(); | |||
} catch (Exception e) { | |||
return; | |||
} | |||
line.setLine(points.get(0), points.get(1)); | |||
g2.setPaint(c); | |||
g2.setStroke(new BasicStroke(3)); | |||
g2.draw(line); | |||
} | |||
/** | |||
* Repaint view | |||
* @param g Something special | |||
*/ | |||
@Override | |||
public void paintComponent(Graphics g) | |||
{ | |||
super.paintComponent(g); | |||
if (model == null) { | |||
lg.warning("Model not initialized"); | |||
return; | |||
} | |||
Graphics2D g2 = (Graphics2D) g; | |||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |||
redrawFigures(model.getIntern(), colorInternal, g2); | |||
redrawFigures(model.getExtern(), colorExternal, g2); | |||
} | |||
/** | |||
* Redraw figure | |||
* @param figures Figure to redraw | |||
* @param g2 Graphics context | |||
*/ | |||
private void redrawFigures(Figures figures, Color c, Graphics2D g2) | |||
{ | |||
for (Figure f : figures.getFigures()) { | |||
List<Point> points = f.getPoints(); | |||
for (int i=1; i<points.size(); i++) { | |||
line.setLine(points.get(i-1), points.get(i)); | |||
g2.setPaint(c); | |||
g2.setStroke(new BasicStroke(3)); | |||
g2.draw(line); | |||
} | |||
} | |||
} | |||
/** | |||
* Try to print current graphics | |||
*/ | |||
public void doPrint() | |||
{ | |||
HashPrintRequestAttributeSet printSet = new HashPrintRequestAttributeSet(); | |||
printSet.add(DialogTypeSelection.NATIVE); | |||
PrinterJob pj = PrinterJob.getPrinterJob(); | |||
pj.setPrintable(this); | |||
// Druckdialog | |||
if (pj.printDialog(printSet)) { | |||
try { | |||
pj.print(printSet); | |||
} catch (Exception ex) { | |||
JOptionPane.showMessageDialog(this, ex.toString()); | |||
} | |||
} | |||
} | |||
/** | |||
* Perform print of graphics | |||
* @param gp Current graphics | |||
* @param pf format of page | |||
* @param pageIndex index of page | |||
* @return result if successful | |||
* @throws PrinterException if something went wrong | |||
*/ | |||
@Override | |||
public int print(Graphics gp, PageFormat pf, int pageIndex) throws PrinterException | |||
{ | |||
Graphics2D g2p = (Graphics2D) gp; | |||
if (pageIndex == 0) { | |||
g2p.translate(pf.getImageableX(), pf.getImageableY()); | |||
g2p.scale(pf.getImageableWidth()/pf.getWidth(), pf.getImageableHeight() / pf.getHeight()); | |||
super.print(g2p); | |||
return Printable.PAGE_EXISTS; | |||
} else { | |||
return Printable.NO_SUCH_PAGE; | |||
} | |||
} | |||
} |